Pytanie (Windows batch) Przejdź wewnątrz, jeśli blok zachowuje się bardzo dziwnie


Jeśli weź następujący fragment kodu wsadowego systemu Windows i uruchom go:

echo foo
if 1 == 1 (
    echo bar
    goto asdf
    :asdf
    echo baz
) else (
    echo quux
)

Wynik, którego oczekiwałbym, to:

foo
bar
baz

Ale zamiast tego otrzymuję:

foo
bar
baz
quux

Jeśli skomentuję goto asdf linia, daje wynik, którego oczekuję. The echo quux linia nigdy nie powinna być egzekutywana, więc dlaczego istnienie tego powodu powoduje to?

AKTUALIZACJA: Jeśli chodzi o to, co warto, oto obejście, które poprawnie wykonuje to, co pierwotnie zamierzałem:

goto BEGIN

:doit
    echo bar
    goto asdf
    :asdf
    echo baz
    goto :EOF

:BEGIN

echo foo
if 1 == 1 (
    call :doit
) else (
    echo quux
)

Jednak to nie odpowiada na moje oryginalne pytanie.


16
2017-12-12 22:06


pochodzenie




Odpowiedzi:


Cel CALL lub GOTO nigdy nie powinien znajdować się wewnątrz instrukcji blokowej w nawiasach. Można to zrobić, ale jak widać, wyniki prawdopodobnie nie będą takie, jakimi chcesz.

Cała konstrukcja IF (...) ELSE (...) jest przetwarzana i ładowana do pamięci przed przetworzeniem którejkolwiek z nich. Innymi słowy, jest logicznie traktowany jako jedna linia kodu. Po przeanalizowaniu CMD.EXE oczekuje wznowienia analizowania poczynając od następnego wiersza po konstrukcji IF / ELSE.

Po fazie analizy złożone polecenie jest wykonywane z pamięci. Klauzula IF jest przetwarzana poprawnie, a klauzula ELSE jest poprawnie pomijana. ALE w klauzuli IF (true) wykonuje się GOTO :asdf, więc CMD.EXE dutifly rozpoczyna skanowanie dla etykiety. Zaczyna się na końcu IF / ELSE i skanuje do dolnej części pliku, pętle z powrotem na górę i skanuje, aż znajdzie etykietę. Etykieta znajduje się w Twojej klauzuli IF, ale skaner etykiet nie wie nic o tych szczegółach. Tak więc, gdy złożone polecenie kończy wykonywanie z pamięci, przetwarzanie wsadowe jest wznawiane od etykiety, a nie od końca złożonego IF / ELSE.

W tym momencie procesor wsadowy widzi i wykonuje kilka następnych linii

    echo baz
) else (
    echo quux
)

Baz jest echem, podobnie jak quux. Ale możesz zapytać: "Dlaczego nie? ) else ( i / lub ) wygenerować błąd składni, ponieważ są one obecnie niezbalansowane i nie są już analizowane jako część większego wyrażenia IF?

To ze względu na sposób ) jest obsługiwane.

Jeśli jest otwarta ( aktywne, gdy ) napotkano, a następnie ) jest przetwarzany zgodnie z oczekiwaniami.

Ale jeśli analizator składni oczekuje polecenia i znajduje ) kiedy nie ma aktywnego otwarcia (, a później ) jest ignorowane, a wszystkie znaki w pozostałej części linii są ignorowane! Skutecznie ) działa teraz jako instrukcja REM.


25
2017-12-12 22:43



+1 Dobre wytłumaczenie, Dave. Krótszy jest: kiedy GOTO jest wykonywane dowolne aktywny  FOR/IF polecenie zostanie anulowane, a plik wsadowy będzie kontynuowany od etykiety. - Aacini


Wszystko, co znajduje się w zestawie nawiasów, traktowane jest jako pojedyncza linia, przetwarzane, interpretowane i wykonywane jednym uderzeniem. Twój skrypt sięga goto asdf i wyskakuje z tego bloku / linii. Na etykiecie :asdf, nie ma nawiasu, więc zaczyna czytać linie jeden po drugim. Dotrze else, ale nie ma if pomiędzy :asdf i else więc to ignoruje.

Aby zapobiec takim problemom, zawsze używam goto lub call na if i for oświadczenia, a nie bloki. To rozwiązuje problemy z dalszym goto oświadczenia, a także rozwiązuje wiele problemów ze zmiennymi.

Używać goto:

echo foo
if 1 == 1 goto bar
echo quux
goto nextbit

:bar
echo bar
goto asdf
:asdf
echo baz

:nextbit
:: more script...

Lub użyć call:

echo foo
if 1 == 1 (call :bar) else (call :quux)
:: more script...
exit /b

:bar
echo bar
goto asdf
:asdf
echo baz
exit /b

:quux
echo quux
exit /b

6
2017-12-12 23:02





W tym przypadku okazuje się, że jest ona związana ze słabym zagnieżdżeniem i strukturą pętli wewnątrz instrukcji IF, co jasno wyjaśniono https://stackoverflow.com/users/1012053/dbenham powyżej.

To powiedziawszy, wymyśliłem inną podobną uwagę ze względu na ten problem ... Proszę spojrzeć na następujący problem i następujące po nim rozwiązanie: "nieoczekiwane w tym czasie" generowane z linii skryptu wsadowego ", jeśli istnieje [plik] (...

Rozwiązaniem było po prostu traktowanie "(" i ")" na liniach ECHO wewnątrz bloku instrukcji IF.

Chodzi o to, rozważ traktowanie znaków specjalnych jako potencjalnego źródła problemu podczas rozwiązywania instrukcji IF (i ewentualnie FOR).

HTH


0
2017-11-14 08:09