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, alefor [...] %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, żeecho.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 wykonamset a=a" b
, a następnieecho.%a%
uzyskama" b
. Jeśli jednak użyję {[21] } Z UnxUtils, dostajęa b
. How comes {[23] } w inny sposób?
Dziękuję za Światła.
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. Faza 1) Procentowa Ekspansja: Faza 1.5) usunąć 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. Następujące znaki mogą mieć specjalne znaczenie w tej fazie, w zależności od kontekstu: Spójrz na każdy znak od lewej do prawo: Faza 3) wyświetla Echo parsowanych poleceń tylko wtedy, gdy blok poleceń nie zaczynał się od Faza 4) dla ---- od tego momentu każde polecenie zidentyfikowane w fazie 2 jest przetwarzane osobno. Faza 5) opóźniona ekspansja: tylko wtedy, gdy opóźniona ekspansja jest włączona Faza 2.3) przetwarzanie rur: tylko wtedy, gdy polecenia znajdują się po obu stronach rury 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ę. Faza 7) wykonanie: polecenie jest wykonywane Działa jak Parser linii Batch, z wyjątkiem: Faza 1) Procentowa Ekspansja: Faza 3) Echo parsowanych poleceń Faza 5) opóźnione rozszerzenie: tylko jeśli włączone jest DelayedExpansion Faza 7) Wykonaj Polecenie 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: Szczegóły tych zasad można znaleźć w zasady dotyczące sposobu CMD.EXE parses numbers Hope it helps
<Ctrl-Z>
, jest traktowana jak sama w sobie - jest Nie konwersja na <LF>
%%
zastępuje się pojedynczym %
%1
, %2
, itd.)%var%
, jeśli var nie istnieje zastąp go przez nic <CR>
: Usuń wszystkie zwroty karetki (0x0d) z linii
<space>
<tab>
;
,
=
<0x0B>
<0x0C>
i <0xFF>
Kolejne ograniczniki tokenów są traktowane jako jeden-między ogranicznikami tokenów nie ma pustych tokenów ^
(
@
&
|
<
>
<LF>
<space>
<tab>
;
,
=
<0x0B>
<0x0C>
<0xFF>
^
), następny znak jest unikany, a karetka jest usuwana. Znaki uciekające tracą wszelkie specjalne znaczenie (z wyjątkiem <LF>
)."
), 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>
.
<LF>
<LF>
jest pozbawiony <LF>
, to jest on traktowany jako literał, co oznacza, że proces ten nie jest rekurencyjny.<LF>
nie w nawiasach
<LF>
jest pozbawiona i parsowanie bieżącej linii jest zakończone.<LF>
wewnątrz bloku FOR w nawiasie
<LF>
jest przekształcane w <space>
<LF>
wewnątrz bloku poleceń w nawiasie
<LF><space>
, a {[13] } jest traktowane jako część następnej linii bloku poleceń.&
|
<
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 &
, &&
, lub ||
konkatenacja polecenia, każda strona konkatenacji jest traktowana jako osobne polecenie.<
, <<
, >
, 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.
@
, wtedy @
ma specjalne znaczenie. ({[1] } nie jest wyjątkowy w żadnym innym kontekście)
@
został usunięty.@
jest przed otwarciem (
, to cały blok z nawiasami jest wyłączony z echa fazy 3.
(
, uruchom nową instrukcję złożoną i zwiększ licznik nawiasów )
kończy wyrażenie złożone i zmniejsza licznik nawiasu.)
funkcje podobne do instrukcji REM
, o ile bezpośrednio po niej następuje ogranicznik tokenu, znak specjalny, znak nowej linii lub koniec pliku
^
(możliwe jest łączenie linii) @
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ń.]}
<LF>
jako <space>
. Po przetworzeniu klauzuli IN wszystkie tokeny są łączone w jeden token.
^
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 ^
.:
, A jest to pierwsza runda Faza 2 (nie restart z powodu wywołania w fazie 6) Następnie
)
, <
, >
, &
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.
(
nie ma już specjalnego znaczenia dla pierwszego polecenia, które następuje po Unexecuted Label w tym kontekście.
|
rura lub &
, &&
, lub ||
konkatenacja polecenia w linii.Etykieta, jej argumenty i przekierowanie są wykluczone z dowolnego wyjścia echa w fazie 3.]}
@
, A ECHO było włączone na początku poprzedniego kroku.%X
rozszerzenie zmiennej: tylko wtedy, gdy polecenie FOR jest aktywne, a polecenia po DO są przetwarzane.
%%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.~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.
---- Fazy od 5 do 7 są zakończone dla jednego polecenia przed przejściem do następnego.
!
. 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
!
są zwinięte do jednego !
!
, których nie można sparować, są usuwane <CR>
lub <LF>
)
Zobacz opóźniona ekspansja zawodzi w niektórych przypadkach
Każda strona rury jest przetwarzana niezależnie.
<LF>
z poleceniem przed i po są konwertowane na <space>&
. Pozostałe <LF>
są rozebrane.%comspec% /S /D /c" commandBlock"
. Oznacza to, że blok poleceń otrzymuje restart fazy, ale tym razem w wierszu poleceń mode.
||
.
/?
. Jeśli zostanie znaleziony w dowolnym miejscu w tokenach, przerwij fazę 6 i przejdź do fazy 7, gdzie zostanie wydrukowana pomoc dla połączenia.CALL
, aby wiele wywołań mogło być ułożonych w stosy
&
or |
(
@
IF
lub FOR
nie jest rozpoznawane jako wewnętrzne lub zewnętrzne polecenie.:
.
Zobacz badanie Linefeeds z wywołaniem
:
, wtedy
Faza 7 nie jest wykonywana dla wywołanych skryptów lub etykiet.
+
/
[
]
<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
.
\
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.+
/
[
]
<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ą./?
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 /?
.
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ść.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.
::
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.
Jeśli woluminu nie można znaleźć, przerwij z błędem.:
, 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 ::
.
:
, 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.:
.
Zasady określone w pkt 7.2 i 7.3 mogą uniemożliwić osiągnięcie tego punktu przez etykietę.
Parser Linii Poleceń:
%var%
jest nadal zastępowana przez zawartość var, ale jeśli var nie jest zdefiniowany, wtedy wyrażenie będzie niezmienione.%%
. If var = content, then %%var%%
rozszerza się do %content%
.
!var!
jest nadal zastępowany przez zawartość var, ale jeżeli var nie jest zdefiniowany, wtedy wyrażenie zostanie niezmienione.
::
Parsowanie wartości całkowitych
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
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.
Jan Eryk (jeb) - autor i odkrywca różnych fazy
Dave Benham (dbenham) - wiele dodatkowych treści i edycji
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.
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]
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
- If batch mode and following by another
-
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!
- If, po którym następuje opcjonalna poprawna lista modyfikatorów argumentów, po której następuje wymagane
- jeśli następuje
- Else if batch mode then
-
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
- If VAR is defined then
- Else goto 1.4
- jeśli następny znak to
- 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
- If VAR is defined then
- Else if next character is
:
then- If VAR is undefined then
- If batch mode then
Usuń%VAR:
i kontynuuj skanowanie. - Else goto 1.4
- If batch mode then
- 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
- jeśli następny ciąg znaków pasuje do wzorca
- 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
- If VAR is undefined then
- jeśli następny znak to
- Else if command extensions are disabled then
-
1.4 (Pasek %)
- Else If batch mode then
Usuń%
i kontynuuj skanowanie - Else preserve
%
and continue scan
- Else If batch mode then
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
- Usuń
- If character is a caret
-
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
- jeśli Var jest zdefiniowany, to
- Else goto 5.2.1
- If next character is
- 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
- jeśli VAR istnieje, to
- 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
- If batch mode then
- 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
- If VAR is undefined then
- Else goto 5.2.1
- If next character is
- 5.2.1
- If batch mode then remove the
!
Else preserve the!
- kontynuuj skanowanie zaczynając od następnego znaku po
!
- If batch mode then remove the
- jeśli rozszerzenia poleceń są wyłączone, to
- jeśli znak jest
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.
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ą"
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 po nim następuje
- jeśli następny znak nie jest
"
, ciąg znaków kończy się
- każdy znak, który nie jest
- 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
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