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?

bezskutecznie
źródło

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"
Zaćmienie
źródło
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 .

mklement0
źródło
Ł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.

bezskutecznie
źródło
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.

TS
źródło
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""')" 
Asjad Azeem
źródło
-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.

XRarach
źródło
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