Własna odpowiedź eplawlessa w prosty i skuteczny sposób rozwiązuje jego specyficzny problem: zastępuje wszystkie "
wystąpienia na całej liście argumentów \"
, co jest sposobem, w jaki Bash wymaga podwójnych cudzysłowów w ciągu znaków w podwójnych cudzysłowach, aby były reprezentowane.
Aby ogólnie odpowiedzieć na pytanie, jak uniknąć podwójnych cudzysłowów w ciągu znaków w podwójnych cudzysłowach za pomocącmd.exe
interpretera wiersza poleceń systemu Windows (czy to w wierszu poleceń - często nadal błędnie nazywanym „monitem DOS” - czy w pliku wsadowym): Zobacz na dole, aby zapoznać się z programem PowerShell .
tl; dr :
Państwo musi użyć""
podczas mijania ciąg do nother) ( plik wsadowy i mogą korzystać ""
z aplikacji utworzonych za pomocą Microsoft C „s / C ++ / net kompilatory. (Które również zaakceptować \"
), która w systemie Windows zawiera Python i node.js :
\"
jest wymagany - jako jedyna opcja - przez wiele innych programów (np. Ruby, Perl, a nawet Microsoft Windows PowerShell (!)), ale JEGO UŻYCIE NIE JEST BEZPIECZNE :
\"
jest tym, czego wymaga wiele plików wykonywalnych i interpreterów - w tym Windows PowerShell - po przekazaniu ciągów z zewnątrz - lub, w przypadku kompilatorów Microsoftu, wsparcie jako alternatywa dla ""
- ostatecznie jednak to program docelowy przeanalizuje listę argumentów .
- Przykład:
foo.exe "We had 3\" of rain."
- Jednakże użycie
\"
wynik może niepożądanych, arbitralne wykonywania poleceń i / lub przekierowania wejścia / wyjścia :
- Następujące postacie stanowią zagrożenie:
& | < >
- Na przykład następujące wyniki powodują niezamierzone wykonanie
ver
polecenia; patrz poniżej, aby uzyskać wyjaśnienie i następny punkt dotyczący obejścia:
foo.exe "3\" of snow" "& ver."
- Dla Windows PowerShell ,
\""
i "^""
są wytrzymałe, ale ograniczone alternatywy (patrz sekcja „Wywołanie CLI PowerShell za ...” poniżej).
Jeśli musisz użyć \"
, są tylko 3 bezpieczne podejścia , które są jednak dość uciążliwe : Nachylenie kapelusza do TS za jego pomoc.
Używając (prawdopodobnie selektywnego ) opóźnionego rozwijania zmiennych w pliku wsadowym, możesz przechowywać literał \"
w zmiennej i odwoływać się do tej zmiennej w ciągu "..."
znaków przy użyciu !var!
składni - zobacz pomocną odpowiedź TS .
- Powyższe podejście, mimo że jest uciążliwe, ma tę zaletę, że można je stosować metodycznie i działa solidnie przy każdym wejściu.
Tylko w przypadku łańcuchów LITERALNYCH - NIE ZAWIERAJĄCYCH ZMIENNYCH - otrzymujesz podobne metodyczne podejście: kategorycznie - ^
pomiń wszystkie cmd.exe
metaznaki: " & | < >
i - jeśli chcesz również wyłączyć rozwijanie zmiennych - %
:
foo.exe ^"3\^" of snow^" ^"^& ver.^"
W przeciwnym razie musisz sformułować swój ciąg w oparciu o rozpoznanie, które części ciągu są cmd.exe
uważane za niecytowane z powodu błędnej interpretacji\"
jako ograniczników zamykających:
w dosłownych porcji zawierających metaznaki powłoki: ^
-escape im; korzystając z powyższego przykładu, &
musi to zostać ^
zmienione:
foo.exe "3\" of snow" "^& ver."
w częściach z %...%
odwołaniami do zmiennych w stylu : upewnij się, że są cmd.exe
one traktowane jako część "..."
łańcucha i że wartości zmiennych same w sobie nie mają osadzonych, niezrównoważonych cudzysłowów - co nie zawsze jest możliwe .
Aby uzyskać dodatkowe informacje, czytaj dalej.
tło
Uwaga: jest to oparte na moich własnych eksperymentach. Daj mi znać, jeśli się mylę.
Powłoki podobne do POSIX, takie jak Bash w systemach uniksowych, tokenizują listę argumentów (łańcuch) przed przekazaniem argumentów indywidualnie do programu docelowego: między innymi dzielą listę argumentów na pojedyncze słowa (dzielenie na słowa) i usuwają znaki cudzysłowu z wynikowe słowa (usuwanie cytatów). Program docelowy podał się tablicę z poszczególnych argumentów , z składniowe usunięte cytaty .
Z kolei interpreter poleceń Windows najwyraźniej nie tokenizuje listy argumentów i po prostu przekazuje pojedynczy ciąg zawierający wszystkie argumenty - w tym znaki cudzysłowu. - do programu docelowego.
Jednak pewne wstępne przetwarzanie ma miejsce, zanim pojedynczy łańcuch zostanie przekazany do programu docelowego: ^
znaki ucieczki. poza podwójnymi cudzysłowami ciągi są usuwane (unikają następnego znaku), a odniesienia do zmiennych (np. %USERNAME%
) są interpolowane jako pierwsze.
Tak więc, w przeciwieństwie do Uniksa, zadaniem programu docelowego jest przeanalizowanie ciągu argumentów i podzielenie go na pojedyncze argumenty z usuniętymi cudzysłowami. Tak więc różne programy mogą hipotetycznie wymagać różnych metod ucieczki i nie ma jednego mechanizmu ucieczki, który gwarantowałby działanie ze wszystkimi programami - https://stackoverflow.com/a/4094897/45375 zawiera doskonałe tło anarchii, jaką jest wiersz poleceń systemu Windows rozbiór gramatyczny zdania.
W praktyce \"
jest to bardzo powszechne, ale NIE BEZPIECZNE , jak wspomniano powyżej:
Ponieważ cmd.exe
sama nie rozpoznaje \"
jako uciekł cudzysłów, może błędnie później żetony w linii poleceń, jak cytowane i potencjalnie interpretować je jako komendy i / lub przekierowania wejścia / wyjścia .
W skrócie: problem powierzchnie, czy którykolwiek z następujących znaków śledzić to otwarcie lub asymetryczne \"
:& | < >
; na przykład:
foo.exe "3\" of snow" "& ver."
cmd.exe
widzi następujące tokeny, wynikające z błędnej interpretacji \"
jako zwykłego cudzysłowu:
"3\"
of
snow" "
- reszta:
& ver.
Ponieważ cmd.exe
uważa, że nie& ver.
jest cytowany , interpretuje to jako &
(operator sekwencjonowania poleceń), po którym następuje nazwa polecenia do wykonania ( ver.
- .
jest ignorowane; ver
informacje cmd.exe
o wersji raportu ).
Ogólny efekt to:
- Po pierwsze,
foo.exe
jest wywoływana tylko za pomocą pierwszych 3 tokenów.
- Następnie polecenie
ver
jest wykonywane.
Nawet w przypadkach, gdy przypadkowe polecenie nie wyrządzi szkody, Twoje ogólne polecenie nie będzie działać zgodnie z przeznaczeniem, biorąc pod uwagę, że nie wszystkie argumenty są do niego przekazywane.
Wiele kompilatorów / interpreterów rozpoznaje TYLKO\"
- np. Kompilator GNU C / C ++, Python, Perl, Ruby, a nawet własny Windows PowerShell Microsoftu, gdy jest wywoływany z cmd.exe
- i z wyjątkiem (z ograniczeniami) dla Windows PowerShell z \""
, dla nich nie ma prostego rozwiązania do tego problemu.
Zasadniczo musiałbyś wiedzieć z góry, które części twojego wiersza poleceń są błędnie interpretowane jako ^
niecytowane , i wybiórczo - unikać wszystkich wystąpień & | < >
w tych fragmentach.
W przeciwieństwie do tego, użycie ""
jest BEZPIECZNE , ale niestety jest obsługiwane tylko przez pliki wykonywalne oparte na kompilatorze firmy Microsoft i pliki wsadowe (w przypadku plików wsadowych, z dziwactwami omówionymi powyżej), co godne uwagi wyklucza PowerShell - patrz następna sekcja.
Wywołanie interfejsu wiersza polecenia programu PowerShell z cmd.exe
lub powłok podobnych do POSIX:
Uwaga: Zobacz dolną sekcję, aby dowiedzieć się, jak obsługiwane jest cytowanie w programie PowerShell.
Wywołane z zewnątrz - np. Z cmd.exe
, czy z wiersza poleceń, czy z pliku wsadowego:
PowerShell [Core] v6 + teraz poprawnie rozpoznaje""
(oprócz\"
), co jest zarówno bezpieczne w użyciu, jak i zachowuje białe znaki .
pwsh -c " ""a & c"".length "
nie pęka i prawidłowo się ugina 6
Windows PowerShell (starsza wersja, której ostatnia wersja to 5.1) rozpoznaje tylko, \"
a w systemie Windows również """
i bardziej niezawodne \""
/"^""
(nawet jeśli wewnętrznie PowerShell używa`
jako znaku ucieczki w podwójnych cudzysłowach, a także akceptuje""
- patrz dolna sekcja):
Wywołanie programu Windows PowerShell zcmd.exe
/ pliku wsadowego:
""
psuje się , ponieważ zasadniczo nie jest obsługiwany:
powershell -c " ""ab c"".length "
-> błąd „W ciągu brakuje terminatora”
\"
i """
działają w zasadzie , ale nie są bezpieczne :
powershell -c " \"ab c\".length "
działa zgodnie z przeznaczeniem: wyprowadza 5
(zwróć uwagę na 2 spacje)
- Ale nie jest to bezpieczne, ponieważ
cmd.exe
metaznaki łamią polecenie, chyba że uciekły:
powershell -c " \"a& c\".length "
przerwy , ze względu na &
, które musiałyby zostać usunięte jako^&
\""
jest bezpieczny , ale normalizuje wewnętrzne spacje , które mogą być niepożądane:
powershell -c " \""a& c\"".length "
wyjścia 4
(!), ponieważ 2 przestrzenie są znormalizowane do 1.
"^""
jest najlepszym wyborem zwłaszcza dla środowiska Windows PowerShell , gdzie jest zarówno bezpieczny, jak i zachowuje białe znaki, ale w przypadku programu PowerShell Core (w systemie Windows) działa tak samo, jak \""
normalizacja białych znaków . Zasługa Venryx do odkrywania tego podejścia.
powershell -c " "^""a& c"^"".length "
działa : nie psuje się - pomimo &
- i wyświetla 5
, czyli poprawnie zachowane białe znaki.
PowerShell Core : pwsh -c " "^""a& c"^"".length "
działa , ale generuje 4
, tj. Normalizuje białe znaki , tak jak \""
robi.
Na platformach typu Unix (Linux, macOS), podczas wywoływania interfejsu wiersza polecenia programu PowerShell [Core]pwsh
, z powłoki podobnej do POSIX, takiej jak bash
:
Państwo musi użyć\"
, która jednak jest zarówno bezpieczna i spacje-konserwujące :
$ pwsh -c " \"a& c|\".length"
Powiązana informacja
^
może być używany tylko jako znak ucieczki w niecytowanych ciągach - wewnątrz ciągów z cudzysłowami, ^
nie jest specjalny i traktowany jako literał.
- PRZESTROGA : Użycie
^
w parametrach przekazanych do call
instrukcji jest zepsute (dotyczy to obu zastosowań call
: wywołania innego pliku wsadowego lub pliku binarnego i wywołania podprogramu w tym samym pliku wsadowym):
^
instancje w podwójnych cudzysłowach są w niewytłumaczalny sposób podwajane , zmieniając przekazywaną wartość: np. jeśli zmienna %v%
zawiera wartość literalną a^b
, call :foo "%v%"
przypisuje "a^^b"
(!) do %1
(pierwszego parametru) w podprogramie :foo
.
- Nienotowanego stosowanie
^
z call
jest uszkodzony całkowicie , że ^
nie mogą już być stosowane do ucieczki znaków specjalnych : npcall foo.cmd a^&b
cicho przerwy (zamiast przechodzenia dosłownea&b
teżfoo.cmd
, jak byłoby to w przypadku bezcall
) -foo.cmd
nigdy nie jest nawet powoływać, przynajmniej na Windows (!) 7.
Unikanie literału %
to niestety szczególny przypadek , który wymaga odmiennej składni w zależności od tego, czy łańcuch jest określony w wierszu poleceń, czy w pliku wsadowym ; zobacz https://stackoverflow.com/a/31420292/45375
- W skrócie: w pliku wsadowym użyj
%%
. W wierszu poleceń %
nie można zastosować zmiany znaczenia, ale jeśli umieścisz a ^
na początku, końcu lub wewnątrz nazwy zmiennej w niecytowanym ciągu znaków (np. echo %^foo%
), Możesz zapobiec interpretacji zmiennej (interpolacji); %
wystąpienia w wierszu poleceń, które nie są częścią odwołania do zmiennej, są traktowane jako literały (np 100%
.).
Ogólnie, aby bezpiecznie pracować z wartościami zmiennymi, które mogą zawierać spacje i znaki specjalne :
- Przypisanie : Umieść zarówno nazwę zmiennej, jak i wartość w jednej parze podwójnych cudzysłowów ; np.
set "v=a & b"
przypisuje wartość dosłowną a & b
do zmiennej %v%
(z set v="a & b"
kolei podwójne cudzysłowy będą częścią wartości). Escape literal %
instances as %%
(działa tylko w plikach wsadowych - patrz wyżej).
- Odniesienie : Podwójne cudzysłowy odwołań do zmiennych, aby upewnić się, że ich wartość nie jest interpolowana; np.
echo "%v%"
nie poddaje wartości %v%
interpolacji i wypisuje "a & b"
(ale pamiętaj, że cudzysłowy są zawsze drukowane). W przeciwieństwie do tego echo %v%
przekazuje literał a
do echo
, interpretuje &
jako operator sekwencjonowania poleceń i dlatego próbuje wykonać polecenie o nazwie b
.
Zwróć również uwagę na powyższe zastrzeżenie dotyczące ponownego użycia ^
w call
oświadczeniu.
- Zewnętrzne programy zazwyczaj usuwają otaczające cudzysłowy otaczające parametry, ale, jak zauważono, w plikach wsadowych musisz to zrobić samodzielnie (np.
%~1
Aby usunąć zamykające cudzysłowy z pierwszego parametru) i niestety nie ma bezpośredniego sposób, który znam, aby echo
wiernie wydrukować wartość zmiennej bez otaczających ją cudzysłowów .
- Neil oferuje obejście oparte na a
for
, które działa, o ile wartość nie ma osadzonych podwójnych cudzysłowów ; na przykład:
set "var=^&')|;,%!"
for /f "delims=" %%v in ("%var%") do echo %%~v
cmd.exe
nie nie rozpoznaje pojedyncze -quotes jako ograniczniki smyczkowych - są traktowane jak literały i generalnie nie może być używany do oddzielania ciągów z wbudowanymi spacji; wynika również z tego, że tokeny stykające się z pojedynczymi cudzysłowami i wszelkie tokeny pomiędzy nimi są traktowane jako niecytowane przez cmd.exe
i odpowiednio interpretowane.
- Jednak biorąc pod uwagę, że programy docelowe ostatecznie przeprowadzają analizę własnych argumentów, niektóre programy, takie jak Ruby, rozpoznają ciągi znaków w apostrofach nawet w systemie Windows; natomiast pliki wykonywalne C / C ++, Perl i Python ich nie rozpoznają.
Jednak nawet jeśli jest obsługiwane przez program docelowy, nie zaleca się używania ciągów znaków w pojedynczych cudzysłowach, ponieważ ich zawartość nie jest chroniona przed potencjalnie niechcianą interpretacją przez cmd.exe
.
Cytowanie z poziomu PowerShell:
Windows PowerShell jest znacznie bardziej zaawansowaną powłoką cmd.exe
i jest częścią systemu Windows od wielu lat (a PowerShell Core wprowadził środowisko PowerShell również do systemów macOS i Linux).
PowerShell działa konsekwentnie wewnętrznie w odniesieniu do cytowania:
- wewnątrz ciągów w podwójnych cudzysłowach użyj
`"
lub, ""
aby uniknąć podwójnych cudzysłowów
- wewnątrz ciągów z pojedynczymi cudzysłowami, użyj
''
do zmiany znaczenia w apostrofach
Działa to w wierszu poleceń programu PowerShell i podczas przekazywania parametrów do skryptów lub funkcji programu PowerShell z poziomu programu PowerShell.
(Jak omówiono powyżej, przekazanie z zewnątrz cudzysłowu uciekającego do programu PowerShell wymaga \"
lub, bardziej niezawodnie, \""
nic innego nie działa).
Niestety, wywołując zewnętrzne programy z PowerShell, stajesz przed koniecznością dostosowania własnych reguł cytowania PowerShell i ucieczki dla programu docelowego :
To problematyczne zachowanie jest również omówione i podsumowane w tej odpowiedzi
Podwójne cudzysłowy w podwójnych cudzysłowach :
Rozważ ciąg "3`" of rain"
, który PowerShell wewnętrznie tłumaczy na literał 3" of rain
.
Jeśli chcesz przekazać ten ciąg do programu zewnętrznego, musisz dodatkowo zastosować znaki ucieczki programu docelowego, oprócz znaków PowerShell ; powiedz, że chcesz przekazać ciąg do programu w C, który oczekuje, że osadzone cudzysłowy zostaną zapisane jako \"
:
foo.exe "3\`" of rain"
Uwaga jak oboje `"
- aby PowerShell Happy - i\
- aby szczęśliwy programu cel - musi być obecny.
Ta sama logika dotyczy wywoływania pliku wsadowego, gdzie ""
należy użyć:
foo.bat "3`"`" of rain"
Z kolei osadzanie pojedynczych cudzysłowów w łańcuchu z podwójnymi cudzysłowami nie wymaga żadnego znaku ucieczki.
Pojedyncze -quotes wewnątrz pojedynczych -quoted strun czy nie wymagają dodatkowych pojemników; rozważ'2'' of snow'
, co jest reprezentacją programu PowerShell2' of snow
.
foo.exe '2'' of snow'
foo.bat '2'' of snow'
PowerShell tłumaczy ciągi w apostrofach na znaki w podwójnych cudzysłowach przed przekazaniem ich do programu docelowego.
Jednak podwójne cudzysłowy w ciągach z pojedynczymi cudzysłowami , które nie wymagają zmiany znaczenia dla programu PowerShell , nadal wymagają zmiany znaczenia dla programu docelowego :
foo.exe '3\" of rain'
foo.bat '3"" of rain'
PowerShell v3 wprowadził magiczną --%
opcję , zwaną symbolem stop-parsing , która łagodzi część bólu, przekazując cokolwiek po niej niezinterpretowane do programu docelowego, z wyjątkiem cmd.exe
odwołań do zmiennych środowiskowych w stylu-style (np. %USERNAME%
), Które są rozwijane; na przykład:
foo.exe --% "3\" of rain" -u %USERNAME%
Zwróć uwagę, że ucieczka do osadzonego, "
jak \"
w przypadku programu docelowego (a nie również w przypadku PowerShell, ponieważ \`"
) jest wystarczająca.
Jednak takie podejście:
- nie pozwala na unikanie
%
znaków, aby uniknąć rozwinięć zmiennych środowiskowych.
- wyklucza bezpośrednie użycie zmiennych i wyrażeń programu PowerShell; zamiast tego wiersz poleceń musi być w pierwszym kroku wbudowany w zmienną łańcuchową, a następnie wywołany za pomocą
Invoke-Expression
w drugim.
Tak więc, pomimo wielu ulepszeń, PowerShell nie znacznie ułatwił ucieczkę podczas wywoływania programów zewnętrznych. Wprowadzono jednak obsługę ciągów w pojedynczych cudzysłowach.
Zastanawiam się, czy w świecie Windows jest zasadniczo możliwe, aby kiedykolwiek przełączyć się na model Unix, pozwalając powłoce wykonać całą tokenizację i wycenić usunięcie w przewidywalny sposób , z góry , niezależnie od programu docelowego , a następnie wywołać program docelowy, przekazując wynikowe tokeny .