Jak Interpreter poleceń systemu Windows (CMD.EXE) parse scripts?

Wpadłem na ss64.com który zapewnia dobrą pomoc w pisaniu skryptów wsadowych uruchamianych przez Interpreter poleceń systemu Windows.

Jednak nie byłem w stanie znaleźć dobrego wyjaśnienia gramatyki skryptów wsadowych, jak rzeczy się rozszerzają lub nie rozszerzają i jak od nich uciec.

Oto przykładowe pytania, których nie udało mi się rozwiązać:]}
  • Jak jest zarządzany system cytatów? Zrobiłem skrypt TinyPerl
    ( foreach $i (@ARGV) { print '*' . $i ; }), skompilował go i nazwał w ten sposób :
    • my_script.exe "a ""b"" c" → wyjście to *a "b*c
    • my_script.exe """a b c""" → output it *"a*b*c"
  • Jak działa wewnętrzne polecenie echo? Co jest rozszerzone wewnątrz tego polecenia?
  • Dlaczego muszę używać for [...] %%I w skryptach plików, ale for [...] %I w sesjach interaktywnych?
  • jakie są znaki ucieczki i w jakim kontekście? Jak uciec przed znakiem procentowym? Na przykład, Jak mogę dosłownie echo %PROCESSOR_ARCHITECTURE%? Znalazłem, że echo.exe %""PROCESSOR_ARCHITECTURE% działa, czy jest lepsze rozwiązanie?
  • jak pary % pasują? Przykład:
    • set b=a , echo %a %b% c%%a a c%
    • set a =b, echo %a %b% c%bb c%
  • Jak zapewnić, że zmienna przechodzi do polecenia jako pojedynczy argument, jeśli kiedykolwiek ta zmienna zawiera podwójne cudzysłowy?
  • jak są przechowywane zmienne podczas korzystania z polecenia set? Na przykład, jeśli wykonam set a=a" b, a następnie echo.%a% uzyskam a" b. Jeśli jednak użyję {[21] } Z UnxUtils, dostaję a b. How comes {[23] } w inny sposób?

Dziękuję za Światła.

Author: Benoit, 2010-11-04

6 answers

Przeprowadziłem wiele eksperymentów w celu zbadania gramatyki skryptów wsadowych. Zbadałem również różnice między trybem wsadowym a wierszem poleceń.

Parser Linii Partii:

Przetwarzanie linii kodu w pliku wsadowym obejmuje wiele faz.

Oto krótki przegląd różnych faz:]}

Faza 0) Czytaj Wiersz:

Faza 1) Procentowa Ekspansja:

Faza 1.5) Usunąć <CR>: Usuń wszystkie znaki powrotu karetki (0x0d)

Faza 2) przetwarza znaki specjalne, tokenizuje i buduje buforowany blok poleceń: {192]} jest to złożony proces, na który mają wpływ takie rzeczy, jak cudzysłowy, znaki specjalne, ograniczniki tokenów i ucieczki karetki.

Faza 3) wyświetla Echo parsowanych poleceń tylko wtedy, gdy blok poleceń nie zaczynał się od @, A ECHO było włączone na początku poprzedniego kroku.

Faza 4) dla %X rozszerzenie zmiennej: tylko wtedy, gdy polecenie FOR jest aktywne, a polecenia po DO są przetwarzane.

W przeciwieństwie do poprzednich wersji, nie jest to możliwe.]}

W przeciwieństwie do innych systemów, nie jest to możliwe.]}

Faza 5.5) Uruchom Przekierowanie:

Faza 6) przetwarzanie/podwojenie wywołania: tylko wtedy, gdy token polecenia jest CALL

Faza 7) Execute: polecenie jest wykonywane


A oto szczegóły dla każdej fazy:

Zauważ, że fazy opisane poniżej są tylko modelem działania parsera wsadowego. Rzeczywisty cmd.exe wewnętrzne mogą nie odzwierciedlać tych faz. Ale ten model jest skuteczny w przewidywaniu zachowania skryptów wsadowych.

Phase 0) Read Line: Read line of input.

  • podczas czytania linii do analizy w zależności od tego, która z tych wartości jest większa, można ją odczytać jako "0x1a" (0x1a).]}
  • gdy GOTO lub CALL odczytuje linie podczas skanowania w poszukiwaniu a :label, <Ctrl-Z>, jest traktowana jak sama w sobie - jest Nie konwersja na <LF>

Faza 1) Procentowa Ekspansja:

  • podwójne %% zastępuje się pojedynczym %
  • rozszerzenie zmiennych argumentów(%1, %2, itd.)
  • rozszerzenie %var%, jeśli var nie istnieje zastąp go przez nic
  • aby uzyskać pełne wyjaśnienie przeczytaj pierwszą połowę tego z dbenham ten sam wątek: Faza procentowa

Faza 1.5) usunąć <CR>: Usuń wszystkie zwroty karetki (0x0d) z linii

Faza 2) przetwarza znaki specjalne, tokenizuje i buduje buforowany blok poleceń: {192]} jest to złożony proces, na który mają wpływ takie rzeczy, jak cudzysłowy, znaki specjalne, ograniczniki tokenów i ucieczki karetki. Co? poniżej przedstawiono przybliżenie tego procesu.

Istnieją pewne pojęcia, które są ważne w tej fazie.

  • token jest po prostu ciągiem znaków, który jest traktowany jako jednostka.
  • żetony są oddzielone ogranicznikami. Standardowe ograniczniki tokenów to <space> <tab> ; , = <0x0B> <0x0C> i <0xFF>
    Kolejne ograniczniki tokenów są traktowane jako jeden-między ogranicznikami tokenów nie ma pustych tokenów
  • są brak ograniczników tokenów w cytowanym łańcuchu. Cały cytowany ciąg jest zawsze traktowany jako część pojedynczego tokena. Pojedynczy token może składać się z kombinacji cytowanych łańcuchów i nienotowanych znaków.

Następujące znaki mogą mieć specjalne znaczenie w tej fazie, w zależności od kontekstu: ^ ( @ & | < > <LF> <space> <tab> ; , = <0x0B> <0x0C> <0xFF>

Spójrz na każdy znak od lewej do prawo:

  • jeśli jest to karetka (^), następny znak jest unikany, a karetka jest usuwana. Znaki uciekające tracą wszelkie specjalne znaczenie (z wyjątkiem <LF>).
  • jeśli jest to quote ("), Przełącz znacznik quote. Jeśli znacznik quote jest aktywny, to tylko " i <LF> są specjalne. Wszystkie pozostałe znaki tracą swoje specjalne znaczenie, dopóki następny cytat nie wyłączy flagi cytatu. Nie jest możliwe uniknięcie cytatu końcowego. Wszystkie cytowane znaki są zawsze w tym samym miejscu.
  • <LF> zawsze wyłącza flagę cytatów. Inne zachowania różnią się w zależności od kontekstu, ale cudzysłowy nigdy nie zmieniają zachowania <LF>.
    • uciekł <LF>
      • <LF> jest pozbawiony
      • następny znak jest uciekany. Jeżeli na końcu bufora linii, wtedy następna linia jest wczytana i dołączana do bieżącej, przed ucieczką od następnego znaku. Jeśli kolejnym znakiem jest <LF>, to jest on traktowany jako literał, co oznacza, że proces ten nie jest rekurencyjny.
    • Unescaped <LF> nie w nawiasach
      • <LF> jest pozbawiona i parsowanie bieżącej linii jest zakończone.
      • pozostałe znaki w buforze linii są po prostu ignorowane.
    • Unescaped <LF> wewnątrz bloku FOR w nawiasie
      • <LF> jest przekształcane w <space>
      • jeśli na końcu bufora linii, to następna linia jest odczytywana i dołączana do bieżącego jeden.
    • Unescaped <LF> wewnątrz bloku poleceń w nawiasie
      • {[4] } jest konwertowane na <LF><space>, a {[13] } jest traktowane jako część następnej linii bloku poleceń.
      • jeśli na końcu bufora linii jest odczytywany następny wiersz i dołączany do spacji.
  • jeśli jest to jeden ze znaków specjalnych& | < lub >, podziel linię w tym miejscu w celu obsługi rur, polecenie konkatenacja i przekierowanie.
      W przypadku pipe (|) każda strona jest oddzielnym poleceniem (lub blokiem poleceń), które otrzymuje specjalną obsługę w fazie 5.3
  • W przypadku &, &&, lub || konkatenacja polecenia, każda strona konkatenacji jest traktowana jako osobne polecenie.
  • W przypadku <, <<, >, lub >> przekierowanie, klauzula przekierowania jest przetwarzana, tymczasowo usuwana, a następnie dołączana do końca bieżącego polecenia. Klauzula przekierowania składa się z opcjonalnej cyfry obsługi pliku, operatora przekierowania i tokenu docelowego przekierowania.
    • jeśli token poprzedzający operatora przekierowania jest pojedynczą cyfrą, to cyfra określa uchwyt pliku, który ma zostać przekierowany. Jeśli Token obsługi nie został znaleziony, to domyślnie przekierowanie wyjściowe wynosi 1 (stdout), a przekierowanie wejściowe 0 (stdin).
  • jeśli pierwszy token tego polecenia (przed przesunięcie przekierowania na koniec) zaczyna się od @, wtedy @ ma specjalne znaczenie. ({[1] } nie jest wyjątkowy w żadnym innym kontekście)
    • specjalny @ został usunięty.
    • jeśli ECHO jest włączone, to to polecenie, wraz z następującymi połączonymi poleceniami w tej linii, są wyłączone z echo fazy 3. Jeśli @ jest przed otwarciem (, to cały blok z nawiasami jest wyłączony z echa fazy 3.
  • nawias procesu (zapewnia polecenia złożone w wielu wierszach):
    • jeśli parser nie szuka tokena polecenia, to {[22] } nie jest specjalny.
    • jeśli parser szuka tokena polecenia i znajduje (, uruchom nową instrukcję złożoną i zwiększ licznik nawiasów
    • jeśli licznik nawiasu jest > 0, to ) kończy wyrażenie złożone i zmniejsza licznik nawiasu.
    • jeśli osiągnięto koniec linii i licznik nawiasów wynosi > 0 następnie następna linia zostanie dołączona do instrukcji złożonej (zaczyna się ponownie od fazy 0)
    • jeśli licznik nawiasów wynosi 0, a parser szuka polecenia, to ) funkcje podobne do instrukcji REM, o ile bezpośrednio po niej następuje ogranicznik tokenu, znak specjalny, znak nowej linii lub koniec pliku
      • wszystkie znaki specjalne tracą swoje znaczenie z wyjątkiem ^ (możliwe jest łączenie linii)
      • po osiągnięciu końca linii logicznej, całe" polecenie " jest odrzucane.
  • każde polecenie jest przetwarzane na serię tokenów. Pierwszy token jest zawsze traktowany jako token polecenia (po usunięciu specjalnych @ i przeniesieniu przekierowania na koniec).
      W tym celu należy wykonać następujące czynności:]} W przeciwieństwie do standardowych ograniczników tokenów, funkcja ta działa jako ogranicznik tokenów poleceń.]}
    • The obsługa kolejnych tokenów zależy od polecenia.
  • większość poleceń po prostu łączy wszystkie argumenty za tokenem polecenia w jeden token argumentu. Wszystkie separatory symboli argumentów są zachowane. Opcje argumentów są zwykle przetwarzane dopiero w fazie 7.
  • trzy komendy mają specjalną obsługę - IF, FOR I REM
    • jeśli jest podzielony na dwie lub trzy odrębne części, które są przetwarzane niezależnie. Błąd składni w konstrukcji IF spowoduje to fatalny błąd składni.
      • operacja porównawcza jest faktycznym poleceniem, które przechodzi aż do fazy 7
        • wszystkie opcje IF są w pełni analizowane w fazie 2.
        • kolejne ograniczniki symboli zapadają się w pojedynczą spację.
        • w zależności od operatora porównania, będzie jeden lub dwa tokeny wartości, które są identyfikowane.
      • True command block jest zestawem poleceń po warunku i jest przetwarzany jak każdy inny blok dowodzenia. Jeśli ma być użyty ELSE, wtedy True block musi być w nawiasie.
      • opcjonalny False Command block jest zestawem poleceń po ELSE. Ponownie, ten blok poleceń jest przetwarzany normalnie.
      • bloki poleceń True I False nie przechodzą automatycznie do kolejnych faz. Ich późniejsze przetwarzanie jest kontrolowane przez fazę 7.
    • FOR dzieli się na dwie części po DO. Błąd składni w konstrukcji FOR spowoduje fatal syntax error.
      • porcja przez DO jest rzeczywistym poleceniem iteracji, które przepływa przez fazę 7
        • wszystkie opcje FOR są w pełni analizowane w fazie 2.
        • klauzula w nawiasie traktuje <LF> jako <space>. Po przetworzeniu klauzuli IN wszystkie tokeny są łączone w jeden token.
        • kolejne nieoznaczone / nieoznaczone ograniczniki tokenów zapadają się w pojedynczą przestrzeń w całym poleceniu FOR poprzez Zrób.
      • część po DO jest blokiem poleceń, który jest przetwarzany normalnie. Kolejne przetwarzanie bloku poleceń DO jest kontrolowane przez iterację w fazie 7.
    • REM wykryty w fazie 2 jest traktowany diametralnie inaczej niż wszystkie inne polecenia.
      • przetwarzany jest tylko jeden argument token - parser ignoruje znaki po pierwszym argumencie token.
      • jeśli istnieje tylko jeden argument token, który kończy się unescaped ^ kończy linię, a następnie argument token jest odrzucany, a następna linia jest przetwarzana i dołączana do REM. To powtarza się, dopóki nie będzie więcej niż jednego tokena, lub ostatnim znakiem nie jest ^.
      • polecenie REM może pojawić się na wyjściu fazy 3, ale polecenie nigdy nie jest wykonywane, a oryginalny tekst argumentu jest wyświetlany echem - nie są usuwane karetki zabezpieczające.
  • jeśli token polecenia zaczyna się od :, A jest to pierwsza runda Faza 2 (nie restart z powodu wywołania w fazie 6) Następnie
    • token jest zwykle traktowany jako nieekranowana Etykieta .
      • reszta linii jest jednak parsowana ), <, >, & i | nie mają już specjalnego znaczenia. Cała pozostała część wiersza jest uważana za część etykiety "command".
      • ^ nadal jest specjalny, co oznacza, że kontynuacja linii może być użyta do dodania kolejnej linii do Etykieta.
      • nieużywana Etykieta w bloku z nawiasami spowoduje błąd krytycznej składni, chyba że po nim natychmiast nastąpi polecenie lub wykonana Etykieta w następnej linii.
        • zauważ, że ( nie ma już specjalnego znaczenia dla pierwszego polecenia, które następuje po Unexecuted Label w tym kontekście.
      • polecenie jest przerywane po zakończeniu parsowania etykiet. Kolejne fazy nie odbywają się dla Etykieta
    • istnieją trzy wyjątki, które mogą spowodować, że etykieta znaleziona w fazie 2 będzie traktowana jakowykonywana Etykieta , która kontynuuje parsowanie przez fazę 7.
      • jest przekierowanie, które poprzedza Token etykiety, i jest | rura lub &, &&, lub || konkatenacja polecenia w linii.
      • istnieje przekierowanie, które poprzedza Token etykiety, a polecenie znajduje się w bloku nawiasowym.
      • Token etykiety jest pierwsze polecenie w linii w bloku z nawiasami, a wiersz powyżej zakończył się etykietą Unexecuted.
    • następuje, gdy wykonana Etykieta zostanie odkryta w fazie 2
        Etykieta, jej argumenty i przekierowanie są wykluczone z dowolnego wyjścia echa w fazie 3.]}
      • wszelkie kolejne połączone polecenia w linii są w pełni przetwarzane i wykonywane.
    • aby uzyskać więcej informacji o wykonane etykiety vs. niezakłócone etykiety , zobacz https://www.dostips.com/forum/viewtopic.php?f=3&t=3803&p=55405#p55405
  • Faza 3) wyświetla Echo parsowanych poleceń tylko wtedy, gdy blok poleceń nie zaczynał się od @, A ECHO było włączone na początku poprzedniego kroku.

    Faza 4) dla %X rozszerzenie zmiennej: tylko wtedy, gdy polecenie FOR jest aktywne, a polecenia po DO są przetwarzane.

    • w tym momencie, faza 1 przetwarzania wsadowego będzie już przekonwertowana na zmienną typu %%X na %X. Wiersz poleceń ma różne reguły rozszerzania procentowego dla fazy 1. Z tego powodu linie poleceń używają %X, natomiast pliki wsadowe używają %%X dla zmiennych FOR.
    • dla nazw zmiennych są rozróżniane wielkość liter, ale ~modifiers nie są rozróżniane wielkość liter.
    • ~modifiers ma pierwszeństwo przed nazwami zmiennych. Jeśli znak po ~ jest zarówno modyfikator i poprawna dla nazwy zmiennej, i istnieje kolejny znak, który jest aktywny dla nazwy zmiennej, wtedy znak jest interpretowany jako modyfikator.
    • dla nazw zmiennych są globalne, ale tylko w kontekście klauzuli DO. Jeśli funkcja jest wywoływana z klauzuli For DO, to zmienne FOR nie są rozwijane w wywołanej funkcji. Ale jeśli rutyna ma swoje własne polecenie, to wszystkie obecnie zdefiniowane dla zmiennych są dostępne dla wewnętrznych poleceń DO.
    • dla nazw zmiennych mogą być ponownie użyte w zagnieżdżonym FORs. Wewnętrzna wartość FOR ma pierwszeństwo, ale gdy wewnętrzna wartość for się zamknie, wtedy zewnętrzna wartość FOR zostanie przywrócona.
    • jeśli ECHO było włączone na początku tej fazy, to faza 3) jest powtarzana, aby pokazać przetworzone polecenia DO po rozszerzeniu zmiennych FOR.

    ---- od tego momentu każde polecenie zidentyfikowane w fazie 2 jest przetwarzane osobno.
    ---- Fazy od 5 do 7 są zakończone dla jednego polecenia przed przejściem do następnego.

    Faza 5) opóźniona ekspansja: tylko wtedy, gdy opóźniona ekspansja jest włączona

    • jeśli polecenie znajduje się w bloku z nawiasami po obu stronach rury, pomiń ten krok.
    • każdy token polecenia jest przetwarzany dla opóźnionego rozszerzenia niezależnie.
      • większość poleceń przetwarza dwa lub więcej tokenów-Token polecenia, argumenty token, a każdy token docelowy przekierowania.
      • polecenie FOR przetwarza tylko token klauzuli IN.
      • polecenie IF przetwarza tylko wartości porównania - jedną lub dwie, w zależności od operatora porównania.
    • dla każdego przetwarzanego tokena, najpierw sprawdź, czy zawiera on jakiś !. Jeśli nie, wtedy token nie jest przetwarzany-ważne dla znaków ^. Jeśli token zawiera !, przeskanuj każdy znak od lewej do prawo:
        Jeśli jest to caret (^) następny znak nie ma specjalnego znaczenia, sam caret jest usuwany
    • jeśli jest to wykrzyknik, wyszukaj następny wykrzyknik (karetki nie są już obserwowane), rozwiń do wartości zmiennej.
      • kolejne otwarcie ! są zwinięte do jednego !
      • wszelkie pozostałe !, których nie można sparować, są usuwane
    • ważne: na tym etapie cytaty i inne znaki specjalne są ignorowane]} Na tym etapie Rozszerzanie VAR-ów jest "bezpieczne" , ponieważ znaki specjalne nie są już wykrywane (nawet <CR> lub <LF>)
    • aby uzyskać pełniejsze Wyjaśnienie, przeczytaj 2. połowę tego z dbenham ten sam wątek-wykrzyknik
    • istnieją pewne skrajne przypadki, w których zasady te wydają się zawodzić:
      Zobacz opóźniona ekspansja zawodzi w niektórych przypadkach

    Faza 2.3) przetwarzanie rur: tylko wtedy, gdy polecenia znajdują się po obu stronach rury
    Każda strona rury jest przetwarzana niezależnie.

    • jeśli mamy do czynienia z blokiem poleceń w nawiasie, to wszystkie <LF> z poleceniem przed i po są konwertowane na <space>&. Pozostałe <LF> są rozebrane.
    • polecenie (lub blok poleceń) jest wykonywane asynchronicznie w nowym cmd.exe thread via
      %comspec% /S /D /c" commandBlock". Oznacza to, że blok poleceń otrzymuje restart fazy, ale tym razem w wierszu poleceń mode.
    • to koniec przetwarzania poleceń pipe.
    • aby uzyskać więcej informacji na temat sposobu parsowania i przetwarzania rur, spójrz na to pytanie i odpowiedzi: dlaczego opóźniona ekspansja zawodzi, gdy wewnątrz piped bloku kodu?

    Faza 5.5) Uruchom przekierowanie: każde przekierowanie Odkryte w fazie 2 jest teraz wykonywane.

    Faza 6) przetwarzanie wywołań / podwojenie karetki: tylko jeśli token polecenia jest CALL, lub jeśli tekst przed pierwszym występującym standardowym ogranicznikiem tokenu jest CALL. Jeśli wywołanie jest przetwarzane z większego tokenu polecenia, to nieużywana część jest poprzedzana tokenem argumentów przed kontynuuję.

    • przeskanuj token argumentów w poszukiwaniu Nie cytowanego /?. Jeśli zostanie znaleziony w dowolnym miejscu w tokenach, przerwij fazę 6 i przejdź do fazy 7, gdzie zostanie wydrukowana pomoc dla połączenia.
    • Usuń pierwsze CALL, aby wiele wywołań mogło być ułożonych w stosy
    • podwój wszystkie karetki
    • Uruchom ponownie fazy 1, 1.5 i 2, ale nie kontynuuj fazy 3
      • podwojone karetki są zredukowane z powrotem do jednego karetki, o ile nie są cytowane. Ale niestety, cytowane karetki pozostają podwojone.
      • buforowany blok poleceń został już przygotowany w pierwszej rundzie fazy 2. Tak wiele zadań fazy 2 jest zmienianych
        • każde nowo pojawiające się nieocenzurowane, nieocenzurowane przekierowanie, które nie zostało wykryte w pierwszej rundzie fazy 2, jest wykrywane, ale jest usuwane (łącznie z nazwą pliku) bez rzeczywistego wykonywania przekierowania
        • każdy nowo pojawiający się nieocenzurowany, nieocenzurowany karetka na końcu wiersza jest usunięto bez wykonywania kontynuacji linii
        • wywołanie zostanie przerwane bez błędu, jeśli zostanie wykryta jedna z poniższych
          • Newly appearing unquoted, unescaped & or |
          • wynikowy token polecenia zaczyna się od unquoted, unescaped (
          • pierwszy token po usuniętym wywołaniu zaczynał się od @
        • jeśli wynikowe polecenie jest pozornie poprawne IF lub FOR, to wykonanie zakończy się błędem stwierdzenie, że IF lub FOR nie jest rozpoznawane jako wewnętrzne lub zewnętrzne polecenie.
        • oczywiście wywołanie nie zostanie przerwane w drugiej rundzie fazy 2, jeśli wynikowy token polecenia jest etykietą zaczynającą się od :.
      • istnieją pewne skrajne przypadki, w których te zasady zawodzą:
        Zobacz badanie Linefeeds z wywołaniem
    • jeśli wynikowym tokenem polecenia jest CALL, to Uruchom ponownie fazę 6 (powtarza się aż nie CALL)
    • jeśli wynikowym tokenem polecenia jest skrypt wsadowy lub etykieta:, to wykonanie wywołania jest w pełni obsługiwane przez pozostałą część fazy 6.
      • przesuwa aktualną pozycję pliku skryptu wsadowego na stosie połączeń, aby wznowić wykonywanie z prawidłowej pozycji po zakończeniu połączenia.
      • Setup the %0, %1, %2, ...Tokeny argumentu %N i %* dla wywołania, przy użyciu wszystkich tokenów wynikowych
      • jeśli Token polecenia jest etykietą zaczynającą się od :, wtedy
        • Uruchom Ponownie Fazę 5. Może to mieć wpływ na nazwę what: label. Ale od %0 itd. tokeny zostały już ustawione, nie zmieni to argumentów przekazywanych do wywołanej procedury.
        • wykonaj GOTO label, aby ustawić wskaźnik pliku na początku podprogramu (ignoruj wszelkie inne tokeny, które mogą podążać za: label) zobacz fazę 7, aby poznać zasady działania GOTO.
      • Else przenosi sterowanie do podanego skryptu wsadowego.
      • Wykonywanie wywołanej etykiety lub skryptu jest kontynuowane do zakończenia / B lub zakończenia pliku, w którym to momencie stos wywołań jest wyrzucany i wznawia się wykonywanie z pozycji zapisanego pliku.
        Faza 7 nie jest wykonywana dla wywołanych skryptów lub etykiet.
    • w przeciwnym razie wynik fazy 6 przechodzi do fazy 7 dla egzekucja.

    Faza 7) wykonanie: polecenie jest wykonywane

    • 7.1-wykonaj wewnętrzne polecenie - jeśli Token polecenia jest cytowany, pomiń ten krok. W przeciwnym razie spróbuj przetworzyć wewnętrzne polecenie i wykonaj.
      • następujące testy są wykonywane w celu ustalenia, czy nienotowany token polecenia reprezentuje wewnętrzne polecenie:
        • jeśli token polecenia dokładnie pasuje do wewnętrznego polecenia, wykonaj to.
        • w przeciwnym wypadku należy przerwać token polecenia przed pierwszym wystąpieniem + / [ ] <space> <tab> , ; lub =
          Jeśli poprzedzający tekst jest poleceniem wewnętrznym, to pamiętaj, że polecenie
            W przypadku, gdy polecenie jest używane w trybie wiersza poleceń lub jeśli polecenie pochodzi z bloku w nawiasie, jeśli jest to prawda lub FAŁSZ, dla bloku polecenia DO lub jest związane z konkatenacją polecenia, wykonaj wewnętrzne polecenie
        • Else (musi być samodzielnym poleceniem w tryb wsadowy) skanowanie bieżącego folderu i ścieżki dla .COM, .EXE,BAT, lub .Plik CMD, którego nazwa bazowa odpowiada oryginalnemu tokenowi polecenia
          • jeśli pierwszy pasujący plik to a .BAT lub .CMD, potem goto 7.3.exec i wykonać ten skrypt
          • Else (match not found or first match is .EXE lub. COM) wykonaj zapamiętaną komendę wewnętrzną
      • w przeciwnym wypadku należy przerwać token polecenia przed pierwszym wystąpieniem . \ lub :
        Jeśli poprzedzający tekst nie jest wewnętrzną komendą, wtedy goto 7.2
        W przeciwnym razie poprzedzający tekst może być poleceniem wewnętrznym. Zapamiętaj to polecenie.
      • Przerwij Token polecenia przed pierwszym wystąpieniem + / [ ] <space> <tab> , ; lub =
        Jeśli poprzedni tekst jest ścieżką do istniejącego pliku, to goto 7.2
        W przeciwnym razie wykonaj zapamiętaną komendę wewnętrzną.
    • jeśli wewnętrzne polecenie jest przetwarzane z większego tokena polecenia, następnie nieużywana część tokenu polecenia jest dołączana do listy argumentów
    • to, że token polecenia jest przetwarzany jako wewnętrzne polecenie, nie oznacza, że zostanie pomyślnie wykonany. Każde wewnętrzne polecenie ma swoje własne reguły dotyczące sposobu parsowania argumentów i opcji oraz dozwolonej składni.
    • wszystkie wewnętrzne polecenia będą drukować help zamiast wykonywać swoją funkcję, jeśli /? zostanie wykryta. Większość rozpoznaje /?, jeśli pojawia się gdziekolwiek w argumentach. Ale kilka poleceń, takich jak ECHO I SET tylko Drukuj pomoc, jeśli pierwszy argument token zaczyna się od /?.
    • zestaw ma ciekawą semantykę:
      • jeśli polecenie SET zawiera cudzysłów przed nazwą zmiennej
        set "name=content" ignored --> wartość=content
        następnie tekst pomiędzy pierwszym znakiem równości i ostatnim cytatem jest używany jako treść (wykluczony pierwszy równy i ostatni cytat). Tekst po ostatnim cytacie jest ignorowany. Jeśli po znaku równości nie ma cudzysłowu, to reszta linia jest używana jako treść.
      • jeśli polecenie SET nie ma cudzysłowu przed nazwą
        set name="content" not ignored --> wartość="content" not ignored
        następnie cała pozostała część wiersza po równości jest używana jako treść, łącznie ze wszystkimi cudzysłowami, które mogą być obecne.
    • porównywanie IF jest oceniane i w zależności od tego, czy warunek jest true czy false, przetwarzany jest odpowiedni już przetworzony zależny blok poleceń, począwszy od fazy 5.
    • klauzula IN polecenia FOR jest odpowiednio iterowana.
      • jeśli jest to FOR / F, który iteruje wyjście bloku poleceń, to:
        • klauzula IN jest wykonywana w nowym cmd.proces exe poprzez CMD / C.
        • blok poleceń musi przejść przez cały proces parsowania po raz drugi, ale tym razem w kontekście wiersza poleceń
        • ECHO rozpocznie się, a opóźniona ekspansja Zwykle rozpocznie się wyłączona (w zależności od rejestru ustawienie)
        • wszystkie zmiany środowiska wprowadzone przez blok poleceń in clause zostaną utracone, gdy cmd potomka.proces exe kończy się
      • dla każdej iteracji:
        • wartości zmiennej FOR są zdefiniowane
        • już przetworzony blok poleceń DO jest następnie przetwarzany, począwszy od fazy 4.
    • GOTO używa następującej logiki, aby zlokalizować Etykietę:
      • etykieta jest przetwarzana z pierwszego argumentu token
      • skrypt jest skanowany pod kątem następnego wystąpienia etykiety
        • skanowanie rozpoczyna się od bieżącej pozycji pliku
        • jeśli zostanie osiągnięty koniec pliku, skanowanie powróci do początku pliku i będzie kontynuowane do pierwotnego punktu początkowego.
      • skanowanie zatrzymuje się przy pierwszym wystąpieniu znalezionej etykiety, a wskaźnik pliku jest ustawiany na linię bezpośrednio po etykiecie. Wykonanie skryptu wznawia się od o to chodzi. Zauważ, że pomyślny true GOTO natychmiast przerywa każdy przetworzony blok kodu, w tym pętle FOR.
      • jeśli etykieta nie zostanie znaleziona lub brakuje tokenu etykiety, to polecenie GOTO nie powiedzie się, zostanie wydrukowany komunikat o błędzie i pojawi się stos wywołań. Funkcja ta działa efektywnie jako EXIT / B, z tym wyjątkiem, że wszystkie już przetworzone polecenia w bieżącym bloku poleceń, które następują po GOTO, są nadal wykonywane, ale w kontekście wywołującego (kontekst, który istnieje po zakończeniu / B)
      • Zobacz https://www.dostips.com/forum/viewtopic.php?f=3&t=3803 dla dokładniejszego opisu reguł używanych do parsowania etykiet.
    • Zmień nazwę i skopiuj obie Akceptuj symbole wieloznaczne dla ścieżki źródłowej i docelowej. Ale Microsoft robi straszną robotę dokumentując, jak działają symbole wieloznaczne, zwłaszcza dla ścieżki docelowej. Użyteczny zestaw reguł wieloznacznych można znaleźć w jak interpretuje polecenie Windows RENAME wildcards?
  • 7.2-wykonaj zmianę woluminu - W przeciwnym razie jeśli token polecenia nie zaczyna się od cudzysłowu, ma dokładnie dwa znaki, a drugi znak jest dwukropkiem, Zmień wolumin
    • wszystkie tokeny argumentów są ignorowane
    • jeśli nie można znaleźć woluminu określonego przez pierwszy znak, przerwij z błędem
    • token polecenia :: zawsze spowoduje błąd, chyba że SUBST zostanie użyty do zdefiniowania objętość dla ::
      Jeśli SUBST zostanie użyty do zdefiniowania woluminu dla ::, wolumin zostanie zmieniony, nie będzie traktowany jako etykieta.
  • 7.3-Execute external command - W przeciwnym razie spróbuj traktować polecenie jako polecenie zewnętrzne.
    • Jeśli 2. Znak tokenu polecenia jest dwukropkiem, można sprawdzić wolumin określony przez 1. znak.
      Jeśli woluminu nie można znaleźć, przerwij z błędem.
    • jeśli w tryb wsadowy i token polecenia zaczyna się od :, a następnie goto 7.4
      Zauważ, że jeśli token etykiety zaczyna się od ::, to nie zostanie osiągnięty, ponieważ poprzedni krok zostanie przerwany z błędem, chyba że SUBST zostanie użyty do zdefiniowania woluminu dla ::.
    • Zidentyfikuj zewnętrzne polecenie do wykonania.
      • jest to złożony proces, który może obejmować bieżący wolumin, bieżący katalog, zmienną PATH, zmienną PATHEXT i skojarzenia plików.
      • jeśli nie można zidentyfikować poprawnego polecenia zewnętrznego, a następnie przerwać z błędem.
    • jeśli w trybie wiersza poleceń i token polecenia zaczyna się od :, to goto 7.4
      Należy zauważyć, że jest to rzadko osiągane, ponieważ poprzedni krok zostanie przerwany z błędem, chyba że token polecenia zaczyna się od ::, A SUBST jest używany do definiowania woluminu dla ::, a cały token polecenia jest prawidłową ścieżką do zewnętrznego polecenia.
    • 7.3.exec - Execute the polecenie zewnętrzne.
  • 7.4-Ignoruj Etykietę - Ignoruj polecenie i wszystkie jego argumenty, jeśli token polecenia zaczyna się od :.
    Zasady określone w pkt 7.2 i 7.3 mogą uniemożliwić osiągnięcie tego punktu przez etykietę.

  • Parser Linii Poleceń:

    Działa jak Parser linii Batch, z wyjątkiem:

    Faza 1) Procentowa Ekspansja:

    • %var% jest nadal zastępowana przez zawartość var, ale jeśli var nie jest zdefiniowany, wtedy wyrażenie będzie niezmienione.
    • brak specjalnego postępowania z %%. If var = content, then %%var%% rozszerza się do %content%.

    Faza 3) Echo parsowanych poleceń

    • nie jest to wykonywane po fazie 2. Jest on wykonywany tylko po fazie 4 dla bloku poleceń FOR DO.

    Faza 5) opóźnione rozszerzenie: tylko jeśli włączone jest DelayedExpansion

    • !var! jest nadal zastępowany przez zawartość var, ale jeżeli var nie jest zdefiniowany, wtedy wyrażenie zostanie niezmienione.

    Faza 7) Wykonaj Polecenie

    • próba wywołania lub GOTO a: label powoduje błąd.
    • nawet jeśli etykiety nie mogą być wywołane, poprawna linia może nadal zawierać etykietę. Jak już udokumentowano w fazie 7, wykonana etykieta może spowodować błąd w różnych scenariuszach.
      • etykiety wykonywane wsadowo mogą powodować błąd tylko wtedy, gdy zaczynają się od ::
      • etykiety wykonywane z linii poleceń prawie zawsze powodują błąd

    Parsowanie wartości całkowitych

    Istnieje wiele różnych kontekstów, w których cmd.exe parsuje wartości całkowite z łańcuchów, a reguły są niespójne:

    • SET /A
    • IF
    • %var:~n,m% (zmienny substring rozszerzenie)
    • FOR /F "TOKENS=n"
    • FOR /F "SKIP=n"
    • FOR /L %%A in (n1 n2 n3)
    • EXIT [/B] n

    Szczegóły tych zasad można znaleźć w zasady dotyczące sposobu CMD.EXE parses numbers


    Dla każdego, kto chce poprawić te zasady, istnieje temat dyskusji na forum DosTips {268]}, gdzie można zgłaszać problemy i zgłaszać sugestie.

    Hope it helps
    Jan Eryk (jeb) - autor i odkrywca różnych fazy
    Dave Benham (dbenham) - wiele dodatkowych treści i edycji

     147
    Author: dbenham,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2018-08-04 13:40:11

    Podczas wywoływania polecenia z okna poleceń, tokenizacja argumentów linii poleceń nie jest wykonywana przez cmd.exe (Alias "powłoka"). Najczęściej tokenizacja jest wykonywana przez nowo utworzone procesy w środowisku uruchomieniowym C/C++, ale niekoniecznie tak jest - na przykład, jeśli nowy proces nie został napisany w C / C++, lub jeśli nowy proces zdecyduje się zignorować argv i przetworzyć surową linię poleceń dla siebie (np. za pomocą GetCommandLine () ). Na poziomie systemu operacyjnego Windows przekazuje wiersze poleceń untokenized jako pojedynczy ciąg znaków do nowych procesów. Jest to w przeciwieństwie do większości powłok * nix, gdzie powłoka tokenizuje argumenty w spójny, przewidywalny sposób przed przekazaniem ich do nowo utworzonego procesu. Wszystko to oznacza, że może wystąpić bardzo rozbieżne zachowanie tokenizacji argumentów w różnych programach w systemie Windows, ponieważ poszczególne programy często biorą tokenizację argumentów w swoje ręce.

    Jeśli to brzmi jak anarchia, to tak jakby jest. Jednakże, ponieważ duża liczba z programów Windows do używają środowiska Microsoft C/C++ runtime argv, może być ogólnie użyteczne zrozumienie Jak MSVCRT tokenizuje argumenty. Oto fragment:
    • argumenty są rozdzielane przez spację, która jest spacją lub tabulatorem.
    • łańcuch otoczony podwójnymi cudzysłowami jest interpretowany jako pojedynczy argument, niezależnie od białej spacji zawartej wewnątrz. Cytowany łańcuch może być osadzony w argumencie. Zauważ, że karetka ( ^ ) jest nie jest rozpoznawany jako znak ucieczki lub ogranicznik.
    • podwójny cudzysłów poprzedzony odwrotnym ukośnikiem,\", jest interpretowany jako dosłowny podwójny cudzysłów (").
    • ukośniki są interpretowane dosłownie, chyba że bezpośrednio poprzedzają podwójny cudzysłów.
    • Jeśli po parzystej liczbie ukośników następuje podwójny cudzysłów, to jeden Ukośnik () jest umieszczony w tablicy argv dla każdej pary ukośników ( \ ), a podwójny cudzysłów ( " ) jest interpretowany jako ogranicznik łańcucha znaków.
    • Jeśli po nieparzystej liczbie ukośników następuje podwójny cudzysłów, to jeden Ukośnik () jest umieszczany w tablicy argv dla każdej pary ukośników ( \ ), a podwójny cudzysłów jest interpretowany jako sekwencja specjalna przez pozostałe ukośniki, powodując umieszczenie literalnego podwójnego cudzysłowu (") w argv.

    Microsoft "język wsadowy" (.bat) nie jest wyjątkiem od tego anarchicznego środowiska i rozwinął swój własny, unikalny Zasady tokenizacji i ucieczki. Wygląda też jak cmd.wiersz polecenia exe wykonuje pewne wstępne przetwarzanie argumentu wiersza poleceń (głównie dla podstawiania zmiennych i interpretacji) przed przekazaniem argumentu do nowo wykonującego procesu. Możesz przeczytać więcej o niskopoziomowych szczegółach języka wsadowego i ucieczki cmd w doskonałych odpowiedziach Jeba i dbenhama na tej stronie.


    Zbudujmy proste narzędzie wiersza poleceń w C i zobaczmy, co mówi o Twoim teście przypadki:

    int main(int argc, char* argv[]) {
        int i;
        for (i = 0; i < argc; i++) {
            printf("argv[%d][%s]\n", i, argv[i]);
        }
        return 0;
    }
    

    (uwagi: argv [0] jest zawsze nazwą pliku wykonywalnego i jest pominięte poniżej dla zwięzłości. Testowane na Windows XP SP3. Skompilowany z Visual Studio 2005.)

    > test.exe "a ""b"" c"
    argv[1][a "b" c]
    
    > test.exe """a b c"""
    argv[1]["a b c"]
    
    > test.exe "a"" b c
    argv[1][a" b c]
    

    I kilka moich własnych testów:

    > test.exe a "b" c
    argv[1][a]
    argv[2][b]
    argv[3][c]
    
    > test.exe a "b c" "d e
    argv[1][a]
    argv[2][b c]
    argv[3][d e]
    
    > test.exe a \"b\" c
    argv[1][a]
    argv[2]["b"]
    argv[3][c]
    
     58
    Author: Mike Clark,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2015-01-13 18:55:35

    Oto rozszerzone Wyjaśnienie fazy 1 w odpowiedzi Jeba (dotyczy zarówno trybu wsadowego, jak i trybu wiersza poleceń).

    Faza 1) Procentowa Ekspansja Zaczynając od lewej, przeskanuj każdy znak w poszukiwaniu %. If found then

    • 1.1 (escape %) skipped if command line mode
      • If batch mode and following by another % then
        Zastąp {[8] } pojedynczym % i kontynuuj scan
    • 1.2 (rozwiń argument) skipped if command line mode
      • Else if batch mode then
        • jeśli następuje * i włączone są rozszerzenia poleceń, to
          Zastąp %* tekstem wszystkich argumentów linii poleceń (Zastąp niczym, jeśli nie ma argumentów) i kontynuuj skanowanie.
        • Else if followed by <digit> then
          Replace %<digit> with argument value (replace with nothing if undefined) and kontynuuj skanowanie.
        • Else if followed by ~ and command extensions are enabled then
          • If, po którym następuje opcjonalna poprawna lista modyfikatorów argumentów, po której następuje wymagane <digit> then
            Zastąp %~[modifiers]<digit> zmodyfikowaną wartością argumentu (zastąp niczym, jeśli nie zdefiniowano lub jeśli nie zdefiniowano modyfikatora $PATH:) i kontynuuj skanowanie.
            Uwaga: modyfikatory są niewrażliwe na wielkość liter i mogą pojawić się wiele razy w dowolnej kolejności, z wyjątkiem $PATH: modyfikator może pojawić się tylko raz i musi być ostatnim modyfikatorem przed <digit>
          • Else nieprawidłowa zmodyfikowana składnia argumentu podnosi błąd krytyczny: wszystkie przetwarzane polecenia są przerywane, a przetwarzanie wsadowe przerywa, jeśli w trybie wsadowym!
    • 1.3 (rozwiń zmienną)
      • Else if command extensions are disabled then
        Spójrz na następny ciąg znaków, łamany przed % lub <LF> i nazwij je VAR (może być pustą listą)
        • jeśli następny znak to % to
          • If VAR is defined then
            Zastąp %VAR% wartością VAR i kontynuuj skanowanie
          • Else if batch mode then
            Usuń %VAR% i kontynuuj skanowanie
          • Else goto 1.4
        • Else goto 1.4
      • Else if command extensions are enabled then
        Spójrz na następny ciąg znaków, łamanie przed % : lub <LF> i wywołaj je VAR (może być pustą listą). If VAR łamanie przed : i kolejnym znakiem jest %, następnie dołącz : jako ostatni znak w VAR i łamanie przed %.
        • jeśli następny znak to % to
          • If VAR is defined then
            Zastąp %VAR% wartością VAR i kontynuuj skanowanie
          • Else if batch mode then
            Usuń %VAR% i kontynuuj skanowanie
          • Else goto 1.4
        • Else if next character is : then
          • If VAR is undefined then
            • If batch mode then
              Usuń %VAR: i kontynuuj skanowanie.
            • Else goto 1.4
          • Else if next character is ~ then
            • jeśli następny ciąg znaków pasuje do wzorca [integer][,[integer]]% to
              Zastąp %VAR:~[integer][,[integer]]% podłańcuchem wartości VAR (ewentualnie pustym łańcuchem) i kontynuuj skanowanie.
            • Else goto 1.4
          • Else if followed by = or *= then
            Nieprawidłowe wyszukiwanie i zamiana zmiennych składnia podnosi błąd krytyczny: wszystkie przetwarzane polecenia są przerywane, a przetwarzanie wsadowe przerywa, jeśli w trybie wsadowym!
          • Else jeśli następny ciąg znaków pasuje do wzorca [*]search=[replace]%, gdzie wyszukiwanie może zawierać dowolny zestaw znaków z wyjątkiem = i <LF>, a zastąp może zawierać dowolny zestaw znaków z wyjątkiem % i <LF>, to zastąp
            %VAR:[*]search=[replace]% z wartością VAR po wykonaniu wyszukiwania i zamiany (ewentualnie w wyniku pustego ciągu) I continue scan
          • Else goto 1.4
    • 1.4 (Pasek %)
      • Else If batch mode then
        Usuń % i kontynuuj skanowanie
      • Else preserve % and continue scan

    Powyższe pomaga wyjaśnić, dlaczego ta partia

    @echo off
    setlocal enableDelayedExpansion
    set "1var=varA"
    set "~f1var=varB"
    call :test "arg1"
    exit /b  
    ::
    :test "arg1"
    echo %%1var%% = %1var%
    echo ^^^!1var^^^! = !1var!
    echo --------
    echo %%~f1var%% = %~f1var%
    echo ^^^!~f1var^^^! = !~f1var!
    exit /b
    

    Daje te wyniki:

    %1var% = "arg1"var
    !1var! = varA
    --------
    %~f1var% = P:\arg1var
    !~f1var! = varB
    

    Uwaga 1 - Faza 1 następuje przed uznaniem oświadczeń REM. To jest bardzo ważne, ponieważ oznacza to, że nawet uwaga może wygenerować błąd krytyczny, jeśli ma nieprawidłową składnię rozszerzenia argumentu lub nieprawidłową składnię wyszukiwania i zamiany zmiennych!

    @echo off
    rem %~x This generates a fatal argument expansion error
    echo this line is never reached
    

    Uwaga 2 - kolejna interesująca konsekwencja reguł % parsingu: zmienne zawierające : w nazwie mogą być zdefiniowane, ale nie mogą być rozszerzone, chyba że rozszerzenia poleceń są wyłączone. Jest jeden wyjątek - nazwa zmiennej zawierająca pojedynczy dwukropek na końcu może być rozszerzona podczas gdy polecenie rozszerzenia są włączone. Nie można jednak wykonywać podciągów lub wyszukiwać i zastępować operacji na nazwach zmiennych zakończonych dwukropkiem. Poniższy plik wsadowy (dzięki uprzejmości jeb) demonstruje to zachowanie

    @echo off
    setlocal
    set var=content
    set var:=Special
    set var::=double colon
    set var:~0,2=tricky
    set var::~0,2=unfortunate
    echo %var%
    echo %var:%
    echo %var::%
    echo %var:~0,2%
    echo %var::~0,2%
    echo Now with DisableExtensions
    setlocal DisableExtensions
    echo %var%
    echo %var:%
    echo %var::%
    echo %var:~0,2%
    echo %var::~0,2%
    

    Uwaga 3 - ciekawy wynik kolejności reguł parsowania, którą Jeb podaje w swoim poście: podczas wykonywania wyszukiwania i zastępowania normalnymi rozszerzeniami, znaki specjalne nie powinny być unikane (choć mogą być cytowane). Ale podczas wykonywania wyszukiwania i zastąpić opóźnionym rozszerzeniem, znaki specjalne muszą być unikane (chyba że są cytowane).

    @echo off
    setlocal enableDelayedExpansion
    set "var=this & that"
    echo %var:&=and%
    echo "%var:&=and%"
    echo !var:^&=and!
    echo "!var:&=and!"
    

    Oto rozszerzone i dokładniejsze wyjaśnienie fazy 5 w odpowiedzi Jeba (dotyczy zarówno trybu wsadowego, jak i trybu wiersza poleceń)

    Zauważ, że istnieją pewne skrajne przypadki, w których te zasady zawodzą:
    Zobacz badanie Linefeeds z wywołaniem

    Phase 5) Delayed Expansion tylko wtedy, gdy włączona jest opcja delayed expansion, a linia zawiera co najmniej jeden !, wtedy Od lewej Skanuj każdy znak w poszukiwaniu ^ lub !, a jeśli zostanie znaleziony, to

    • 5.1 (1) potrzebne do ! lub ^ literałów
      • If character is a caret ^ then
        • Usuń ^
        • Skanuj następny znak i zachowaj go jako literał
        • kontynuuj skanowanie
    • 5.2 (rozwiń zmienną)
      • jeśli znak jest !, wtedy
        • jeśli rozszerzenia poleceń są wyłączone, to
          Spójrz na następny ciąg znaków, łamany przed ! lub <LF> i wywołaj je VAR (może być pustą listą)
          • If next character is ! then
            • jeśli Var jest zdefiniowany, to
              Zastąp !VAR! wartością VAR i kontynuuj skanowanie
            • Else if batch mode then
              Usuń !VAR! i kontynuuj skanowanie
            • Else goto 5.2.1
          • Else goto 5.2.1
        • Else if command extensions are enabled then
          Spójrz na następny ciąg znaków, łamanie przed !, :, lub <LF> i wywołaj je VAR (może być pustą listą). Jeśli VAR łamie się przed :, a kolejnym znakiem jest !, to dodaj : jako ostatni znak w VAR i break przed !
          • If next character is ! then
            • jeśli VAR istnieje, to
              Zastąp !VAR! wartością VAR i kontynuuj scan
            • Else if batch mode then
              Usuń !VAR! i kontynuuj skanowanie
            • Else goto 5.2.1
          • Else if next character is : then
            • If VAR is undefined then
              • If batch mode then
                Usuń !VAR: i kontynuuj skanowanie
              • Else goto 5.2.1
            • Else jeśli następny ciąg znaków pasuje do wzorca
              ~[integer][,[integer]]! wtedy
              Zastąp !VAR:~[integer][,[integer]]! podłańcuchem wartości VAR (ewentualnie w rezultacie otrzymujemy pusty ciąg znaków) i kontynuujemy skanowanie
            • w przeciwnym razie jeśli następny ciąg znaków pasuje do wzorca [*]search=[replace]!, gdzie wyszukiwanie może zawierać dowolny zestaw znaków z wyjątkiem = i <LF>, a zastąp może zawierać dowolny zestaw znaków z wyjątkiem ! i <LF>, to
              Po wykonaniu wyszukiwania i zamianie (ewentualnie w wyniku pustego ciągu znaków) zastąp !VAR:[*]search=[replace]! wartością VAR i kontynuuj skanowanie
            • Else goto 5.2.1
          • Else goto 5.2.1
        • 5.2.1
          • If batch mode then remove the !
            Else preserve the !
          • kontynuuj skanowanie zaczynając od następnego znaku po !
     41
    Author: dbenham,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2018-02-02 20:13:38

    Jak wspomniano, polecenia są przekazywane przez cały łańcuch argumentów w µSoft land, i to do nich należy przetworzenie tego w osobne argumenty na ich własny użytek. Nie ma w tym konsystencji między różnymi programami, a zatem nie ma jednego zestawu reguł opisujących ten proces. Naprawdę musisz sprawdzić każdą skrzynkę narożną pod kątem dowolnej biblioteki C, której używa twój program.

    Jeśli chodzi o pliki systemu .bat, Oto ten test:

    c> type args.cmd
    @echo off
    echo cmdcmdline:[%cmdcmdline%]
    echo 0:[%0]
    echo *:[%*]
    set allargs=%*
    if not defined allargs goto :eof
    setlocal
    @rem Wot about a nice for loop?
    @rem Then we are in the land of delayedexpansion, !n!, call, etc.
    @rem Plays havoc with args like %t%, a"b etc. ugh!
    set n=1
    :loop
        echo %n%:[%1]
        set /a n+=1
        shift
        set param=%1
        if defined param goto :loop
    endlocal
    
    Teraz możemy przeprowadzić kilka testów. Zobacz też jeśli możesz dowiedzieć się, co µSoft próbuje zrobić: {]}
    C>args a b c
    cmdcmdline:[cmd.exe ]
    0:[args]
    *:[a b c]
    1:[a]
    2:[b]
    3:[c]
    
    Jak na razie dobrze. (Od teraz pomijam nieciekawe %cmdcmdline% i %0.)
    C>args *.*
    *:[*.*]
    1:[*.*]
    

    Brak rozszerzenia nazwy pliku.

    C>args "a b" c
    *:["a b" c]
    1:["a b"]
    2:[c]
    

    Brak usuwania cytatów, choć cudzysłowy zapobiegają dzieleniu argumentów.

    c>args ""a b" c
    *:[""a b" c]
    1:[""a]
    2:[b" c]
    

    Kolejne podwójne cudzysłowy powodują utratę specjalnych zdolności parsowania, jakie mogły mieć. Przykład @ Beniot:

    C>args "a """ b "" c"""
    *:["a """ b "" c"""]
    1:["a """]
    2:[b]
    3:[""]
    4:[c"""]
    

    Quiz: Jak przekazać wartość dowolnego var środowiska jako pojedynczy argument (tzn. jako %1) do pliku bat?

    c>set t=a "b c
    c>set t
    t=a "b c
    c>args %t%
    1:[a]
    2:["b c]
    c>args "%t%"
    1:["a "b]
    2:[c"]
    c>Aaaaaargh!
    

    Rozsądne parsowanie wydaje się na zawsze zepsute.

    Dla rozrywki, spróbuj dodać różne ^, \, ', & (&c.) znaków do tych przykładów.

     8
    Author: bobbogo,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2014-08-05 18:48:44

    Masz już kilka świetnych odpowiedzi powyżej, ale aby odpowiedzieć na jedną część swojego pytania:

    set a =b, echo %a %b% c% → bb c%
    

    Dzieje się tak dlatego, że ponieważ masz spację przed=, tworzona jest zmienna o nazwie %a<space>% więc kiedy {[2] } jest poprawnie oceniany jako b.

    Pozostała część b% c% jest następnie oceniana jako zwykły tekst + Niezdefiniowana zmienna % c%, która powinna być powtórzona jako wpisana, dla mnie echo %a %b% c% zwraca bb% c%

    Podejrzewam, że możliwość włączenia spacji w zmiennej nazwa jest bardziej niedopatrzeniem niż planowaną "funkcją"

     5
    Author: ss64,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2014-08-11 21:01:13

    Edit: zobacz akceptowaną odpowiedź, co następuje jest złe i wyjaśnia tylko, jak przekazać linię poleceń do TinyPerl.


    Jeśli chodzi o cytaty, mam wrażenie, że zachowanie jest następujące:]}
    • gdy " zostanie znalezione, string globbing zaczyna się
    • gdy występuje globbing ciągów:
      • każdy znak, który nie jest " jest globbed
      • gdy znaleziono ":
        • jeśli po nim następuje "" (a więc potrójne "), to podwójny cytat jest dodano do łańcucha
        • Jeśli po nim następuje " (a więc podwójne "), to do łańcucha dodawany jest podwójny cudzysłów i końce globbingu
      • jeśli następny znak nie jest ", ciąg znaków kończy się
    • gdy kończy się linia, kończy się globbing.

    W skrócie:

    "a """ b "" c""" składa się z dwóch łańcuchów: a " b " i c"

    "a"", "a""" i"a"""" są tymi samymi łańcuchami, jeśli na końcu linii

     0
    Author: Benoit,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2011-11-01 19:26:18