Unikanie podwójnych cudzysłowów w skrypcie wsadowym


92

Jak powinienem zająć się zastąpieniem wszystkich podwójnych cudzysłowów w parametrach mojego pliku wsadowego cudzysłowami ze znakami ucieczki? To jest mój bieżący plik wsadowy, który rozwija wszystkie parametry wiersza poleceń w ciągu:

@echo off
call bash --verbose -c "g++-linux-4.1 %*"

Następnie używa tego ciągu, aby wywołać bash Cygwina, wykonując cross-kompilator Linuksa. Niestety, otrzymuję parametry takie jak te przekazywane do mojego pliku wsadowego:

"launch-linux-g++.bat" -ftemplate-depth-128 -O3 -finline-functions 
-Wno-inline -Wall  -DNDEBUG   -c 
-o "C:\Users\Me\Documents\Testing\SparseLib\bin\Win32\LinuxRelease\hello.o" 
"c:\Users\Me\Documents\Testing\SparseLib\SparseLib\hello.cpp"

Gdzie pierwszy cudzysłów wokół pierwszej przekazanej ścieżki przedwcześnie kończy ciąg przekazywany do GCC i przekazuje resztę parametrów bezpośrednio do bash (co spektakularnie się nie udaje).

Wyobrażam sobie, że jeśli uda mi się połączyć parametry w jeden ciąg, a następnie uciec przed cudzysłowami, powinno działać dobrze, ale mam trudności z określeniem, jak to zrobić. Czy ktoś wie?

Odpowiedzi:


104

Znakiem ucieczki w skryptach wsadowych jest ^. Ale w przypadku ciągów w podwójnych cudzysłowach podwoj cudzysłowy:

"string with an embedded "" character"

5
podwajanie cudzysłowów nie działało dla mnie, ale ^ działało jak mistrz.
davenpcj

26
^jest znakiem ucieczki tylko w niecytowanych łańcuchach; w podwójnych cudzysłowach jest traktowany jako literał. W przeciwieństwie do powłok uniksowych (podobnych do POSIX), cmd.exeNIE oferuje standardowego przetwarzania przez powłokę podwójnych cudzysłowów wewnątrz łańcucha podwójnego cudzysłowu, a interpretacja jest pozostawiona wywoływanemu programowi (kontynuacja w następnym komentarzu).
mklement0

9
(ciąg dalszy z poprzedniego komentarza) W praktyce większość plików wykonywalnych / interpreterów skryptów stosuje konwencję C, oczekującą "znaków. być wprowadzane jako znaki ucieczki jako \"wewnątrz ciągu znaków umieszczonych w podwójnych cudzysłowach (dotyczy przynajmniej: C / C ++, Python, Perl, Ruby). Z drugiej strony, ""jest rozpoznawany tylko w nielicznych przypadkach: w parametrach przekazywanych do plików wsadowych "" jest rozpoznawany jako osadzony podwójny cudzysłów, ale jest zachowywany tak, jak w odpowiednim %<n>parametrze, nawet po usunięciu otaczających cudzysłowów %~<n>. Python łaskawie rozpoznaje również"" , jako alternatywę dla \".
mklement0

90

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 :

    • Przykład: foo.bat "We had 3"" of rain."

    • Poniższe informacje dotyczą tylko plików wsadowych:

      • ""jest jedynym sposobem na to, aby interpreter polecenia ( cmd.exe) traktował cały łańcuch w cudzysłowie jako pojedynczy argument.

      • Niestety, nie tylko otaczające cudzysłowy są zachowywane (jak zwykle), ale także podwójne znaki ucieczki, więc uzyskanie żądanego ciągu jest procesem dwuetapowym; na przykład, zakładając, że ciąg dwukrotnie cytowany jest przekazywana jako 1st argumentu %1:

      • set "str=%~1"usuwa otaczające cudzysłowy; set "str=%str:""="%"następnie konwertuje podwójne cudzysłowy na pojedyncze.
        Pamiętaj, aby używać otaczających cudzysłowów wokół części przydziału, aby zapobiec niepożądanej interpretacji wartości.

  • \"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 verpolecenia; 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.exeuważ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.exeone 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.exesama 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.exewidzi następujące tokeny, wynikające z błędnej interpretacji \"jako zwykłego cudzysłowu:

  • "3\"
  • of
  • snow" "
  • reszta: & ver.

Ponieważ cmd.exeuważa, że nie& ver. jest cytowany , interpretuje to jako &(operator sekwencjonowania poleceń), po którym następuje nazwa polecenia do wykonania ( ver.- .jest ignorowane; verinformacje cmd.exeo wersji raportu ).
Ogólny efekt to:

  • Po pierwsze, foo.exejest wywoływana tylko za pomocą pierwszych 3 tokenów.
  • Następnie polecenie verjest 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.exelub 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.exemetaznaki ł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" # OK: 5

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 callinstrukcji 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 calljest uszkodzony całkowicie , że ^nie mogą już być stosowane do ucieczki znaków specjalnych : npcall foo.cmd a^&bcicho przerwy (zamiast przechodzenia dosłownea&bteżfoo.cmd, jak byłoby to w przypadku bezcall) -foo.cmdnigdy 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 & bdo 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ł ado 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 calloś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. %~1Aby usunąć zamykające cudzysłowy z pierwszego parametru) i niestety nie ma bezpośredniego sposób, który znam, aby echowiernie 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.exenie 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.exei 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.exei 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.exeodwołań do zmiennych środowiskowych w stylu-style (np. %USERNAME%), Które 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-Expressionw 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 .


Ładny opis! Ale ... Literal use of ^ in double-quoted strings can, be problematic when applying ~ ...nie do końca. Tylda usuwa cudzysłowy zewnętrzne tylko wtedy, gdy są obecne. Utrata karetek jest problemem samej obsługi. Zwykle to set "param=%~1"rozwiązuje.
jeb

Dzięki, @jeb, problem rzeczywiście nie jest specyficzny dla użycia ~- zaktualizowałem moją odpowiedź, aby odzwierciedlała moje nowe rozumienie - proszę o komentarz, jeśli zauważysz problem. Czy masz wyjaśnienie, dlaczego podwojenie ^tego następuje automatycznie, gdy przekazujesz parametry do callinstrukcji?
mklement0

3
Mogę się tylko domyślać, że ktoś ze stwardnienia rozsianego myślał, że to genialne. Że podwojone karetki zostaną automatycznie usunięte w drugiej fazie analizy. Ale to była duża porażka, ponieważ nie działa w cudzysłowach i skutecznie zapobiega ucieczce jakichkolwiek znaków specjalnych. Jakby call echo cat^^&dognie można tego rozwiązać samą ilością karetek
jeb

Dzięki, @jeb, ja nawet nie uważa się nienotowane użycie ^z call, które, jak wskazałeś, jest poważnie uszkodzony. Wygląda na to, że w call echo cat^&dog(pojedynczym, ^który poprawnie wymyka się &) polecenie docelowe ( echo) nigdy nie jest nawet wywoływane (!) - całe polecenie cicho kończy się niepowodzeniem. Odpowiednio zaktualizowałem odpowiedź.
mklement0

Niezła odpowiedź. Jednak nie polecałbym ""jako uciekł, "ale zawsze \"(zobacz moją odpowiedź na mniej niebezpieczny sposób użycia go z poziomu cmd). Nie znam żadnej oficjalnej dokumentacji, który definiuje ""jako zbiegłego cytat, ale co najmniej 2, które wspomnieć \": .NET i VS . Chociaż jest źle udokumentowany, interfejs API Win32 również przestrzega tych zasad.
TS

24

Google w końcu znalazł odpowiedź. Składnia zastępowania ciągów w partii jest następująca:

set v_myvar=replace me
set v_myvar=%v_myvar:ace=icate%

Który tworzy „replikuj mnie”. Mój skrypt wygląda teraz tak:

@echo off
set v_params=%*
set v_params=%v_params:"=\"%
call bash -c "g++-linux-4.1 %v_params%"

Który zastępuje wszystkie wystąpienia "z \", poprawnie uciekł do bash.


9

Jako dodatek do doskonałej odpowiedzi mklement0 :

Prawie wszystkie pliki wykonywalne są akceptowane \" jako uciekające ". Bezpieczne użycie w cmd jest jednak prawie możliwe tylko przy użyciu OPÓŹNIONEGO EKSPANSJI.
Aby jawnie wysłać literał "do jakiegoś procesu, przypisz go \"do zmiennej środowiskowej, a następnie użyj tej zmiennej, ilekroć musisz przekazać cytat. Przykład:

SETLOCAL ENABLEDELAYEDEXPANSION
set q=\"
child "malicious argument!q!&whoami"

Notatka SETLOCAL ENABLEDELAYEDEXPANSIONwydaje się działać tylko w plikach wsadowych. Aby uzyskać OPÓŹNIONĄ EKSPANSJĘ w sesji interaktywnej, zacznij cmd /V:ON.

Jeśli Twój plik wsadowy nie działa z OPÓŹNIONĄ EKSPANSJĄ, możesz ją tymczasowo włączyć:

::region without DELAYEDEXPANSION

SETLOCAL ENABLEDELAYEDEXPANSION
::region with DELAYEDEXPANSION
set q=\"
echoarg.exe "ab !q! & echo danger"
ENDLOCAL

::region without DELAYEDEXPANSION

Jeśli chcesz przekazać dynamiczną zawartość ze zmiennej, która zawiera cudzysłowy zapisane jako "" które możesz zastąpić ""przy \"rozwinięciu:

SETLOCAL ENABLEDELAYEDEXPANSION
foo.exe "danger & bar=region with !dynamic_content:""=\"! & danger"
ENDLOCAL

Ta wymiana nie jest bezpieczna w przypadku %...%rozszerzenia stylu!

W przypadku OP bash -c "g++-linux-4.1 !v_params:"=\"!" jest wersją bezpieczną.


Jeśli z jakiegoś powodu nawet tymczasowe włączenie OPÓŹNIONEGO ROZSZERZENIA nie wchodzi w grę, czytaj dalej:

Używanie \"wewnątrz cmd jest trochę bezpieczniejsze, jeśli zawsze trzeba uciec ze znaków specjalnych, a nie tylko czasami. (Jest mniej prawdopodobne, że zapomnisz o daszku, jeśli jest spójny ...)

Aby to osiągnąć, każdy cytat poprzedza się daszkiem ( ^"), cudzysłowy, które powinny dotrzeć do procesu potomnego w postaci literałów, muszą być dodatkowo poprzedzone znakiem odwrotności ( \^"). WSZYSTKIE metaznaki powłoki również muszą być poprzedzone znakami ucieczki ^, np. &=> ^&; |=> ^|;>=> ^>; itp.

Przykład:

child ^"malicious argument\^"^&whoami^"

Źródło: wszyscy źle cytują argumenty wiersza poleceń , zobacz „Lepsza metoda cytowania”


Aby przekazać dynamiczną zawartość, należy upewnić się, że:
Część polecenia zawierająca zmienną musi być traktowana jako „cytowana” przez cmd.exe(Jest to niemożliwe, jeśli zmienna może zawierać cudzysłowy - nie pisz%var:""=\"% ). Aby to osiągnąć, ostatnia "przed zmienną i pierwsza "po zmiennej nie są ^usuwane. cmd-metaznaki między tymi dwoma "nie mogą zostać zmienione. Przykład:

foo.exe ^"danger ^& bar=\"region with %dynamic_content% & danger\"^"

To nie jest bezpieczne, jeśli %dynamic_content%może zawierać niedopasowane cytaty.


Zrozumiałem, dzięki. Tak, kategoryczne ^unikanie wszystkich metaznaków działa solidnie i może być stosowane bardziej metodycznie (ale jest to oczywiście królewski ból w wybranej części ciała). Odpowiednio zaktualizowałem moją odpowiedź (i przyznałem ci kredyt).
mklement0

@ mklement0 Dziękuję! Tak, to naprawdę jest uciążliwe. Jest to irytujące i nadal zbyt łatwo jest zapomnieć o metaznaku (dlatego najczęściej używam !q!sposobu). Uwaga: Twoja odpowiedź jest nieco niespójne po ostatniej edycji: w pobliżu góry powiedzieć: „Czy nie używać ^"”. Później użyjesz ^"jako części obejścia. Może mógłbyś wyjaśnić oba sposoby? (1) Unikanie wszystkich metaznaków (bardziej metodycznie) / (2) Selektywne unikanie metaznaków w regionach „niecytowanych” (czasami konieczne do przekazania zawartości dynamicznej, np. foo.exe ^"danger ^& bar=\"%dynamic_content%\"^"- w ten sposób zmienna jest cytowana dla cmd)
TS

Słuszna uwaga, dziękuję - zaktualizowano odpowiedź. Wyjaśniłem też, że kompilator MS akceptuje zarówno \"i "". Połączyłem się z Twoją odpowiedzią na temat bardziej zaangażowanego podejścia opartego na zmiennych. Daj mi znać, czy teraz ma to sens.
mklement0

1
@ mklement0 Twoja odpowiedź zawsze miała sens :-) Chciałem tylko przedstawić kilka pomysłów na możliwe ulepszenia. Dodałem również przykład o %dynamic_content%do mojej odpowiedzi. Czy uważasz, że jest wystarczająco szczegółowy, czy muszę wyjaśnić więcej?
TS

3
Super, dzięki za poinformowanie mnie. Dobry pomysł, aby zlokalizować setlocal delayedexpansion, ale powinieneś zakończyć blok z endlocal(bez argumentu). Szczerze, moja głowa zaczęła się kręcić, patrząc na twój Gist. Naprawdę mamy tutaj do czynienia z skrajnymi przypadkami i myślę, że przyszli czytelnicy znajdą wszystko, czego potrzebują, między naszymi dwiema odpowiedziami.
mklement0

0

Jeśli ciąg znajduje się już w cudzysłowie, użyj innego cudzysłowu, aby anulować jego działanie.

echo "Insert tablename(col1) Values('""val1""')" 

-3

Na przykład dla Unreal engine Automation narzędzie uruchamiane z pliku wsadowego - to zadziałało dla mnie

np .: -cmdline = "-Messaging" -device = urządzenie -addcmdline = "- SessionId = sesja -SessionOwner = 'właściciel' -SessionName = 'Build' -dataProviderMode = local -LogCmds = 'LogCommodity OFF' -execcmds = 'lista automatyzacji ; runtests testy + oddzielone + przez + T1 + T2; quit '"-run

Mam nadzieję, że to komuś pomogło, zadziałało dla mnie.


Spróbuj wyodrębnić koncepcję, której się nauczyłeś, którą próbujesz podzielić na jasny przykład, zamiast długiej, przeważnie bez znaczenia, wklejonej kopii z twojej pracy.
run_the_race
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.