Wyjdź z pliku wsadowego z podprogramu

20

Jak mogę wyjść z pliku wsadowego z podprogramu?

Jeśli użyję polecenia EXIT, po prostu wracam do wiersza, w którym wywołałem podprogram, i wykonywanie jest kontynuowane.

Oto przykład:

@echo off
ECHO Quitting...
CALL :QUIT
ECHO Still here!
GOTO END

:QUIT
EXIT /B 1

:END
EXIT /B 0

Wynik:

Quitting...
Still here!

Aktualizacja:

To nie jest właściwa odpowiedź, ale ostatecznie zrobiłem coś w stylu:

@echo off
CALL :SUBROUTINE_WITH_ERROR || GOTO HANDLE_FAIL
ECHO You shouldn't see this!
GOTO END

:SUBROUTINE_WITH_ERROR
ECHO Simulating failure...
EXIT /B 1

:HANDLE_FAIL
ECHO FAILURE!
EXIT /B 1

:END
ECHO NORMAL EXIT!
EXIT /B 0

Instrukcja dwururowa:

CALL :SUBROUTINE_WITH_ERROR || GOTO HANDLE_FAIL

jest skrótem od:

CALL :SUBROUTINE_WITH_ERROR 
IF ERRORLEVEL 1 GOTO HANDLE_FAIL    

Wciąż chciałbym wiedzieć, czy istnieje sposób na wyjście bezpośrednio z podprogramu, zamiast zmuszania CALLER do poradzenia sobie z sytuacją, ale to przynajmniej załatwia sprawę.


Aktualizacja nr 2: Podczas wywoływania podprogramu z innego podprogramu, wywoływanego w powyższy sposób, wywołuję w ten sposób z podprogramów:

CALL :SUBROUTINE_WITH_ERROR || EXIT /B 1

W ten sposób błąd rozprzestrzenia się z powrotem na „główny”, że tak powiem. Główna część partii może następnie obsłużyć błąd za pomocą procedury obsługi błędów GOTO: FAILURE

Brown
źródło

Odpowiedzi:

21

Dodaj to na początku pliku wsadowego:

@ECHO OFF
SETLOCAL

IF "%selfWrapped%"=="" (
  REM this is necessary so that we can use "exit" to terminate the batch file,
  REM and all subroutines, but not the original cmd.exe
  SET selfWrapped=true
  %ComSpec% /s /c ""%~0" %*"
  GOTO :EOF
)

Następnie możesz po prostu zadzwonić:

  • EXIT [errorLevel] jeśli chcesz wyjść z całego pliku
  • EXIT /B [errorLevel] aby wyjść z bieżącego podprogramu
  • GOTO :EOF aby wyjść z bieżącego podprogramu
Merlyn Morgan-Graham
źródło
+1 za faktyczną wzmiankęGOTO :EOF
afrazier
1
Bardzo dobrze. Zrobiłem małą modyfikację, przypisz %~0do zmiennej zamiast true: if not "%selfwrapped%"=="%~0" ( set selfwrapped=%~0 .... ). W ten sposób możesz użyć tej samej sztuczki w wielu skryptach wsadowych, które się nawzajem wywołują.
GolezTrol
To świetne rozwiązanie. Czy uważasz, że warto edytować, aby wyjaśnić, jak to działa? Zajęło mi minutę, aby rozpakować to wszystko i zdałem sobie sprawę, że tak naprawdę to po prostu wywołuje plik wsadowy ( %~0) ze wszystkimi argumentami ( %*) z zagnieżdżonego cmd.exe, i /ssłuży do kontrolowania sposobu, w jaki %ComSpec%argument obsługuje podwójne cudzysłowy wokół połączenie.
Sean
@ Sean Uważam, że zwięzłość jest bardziej przydatna dla większości ludzi. Więcej dokumentów nie było pytanych przez 7 lat, odkąd to napisałem, więc wydaje się, że nie jest bardzo poszukiwany. Myślę też, że ludzie mają pewną wartość, aby sami szukali rzeczy, a nie kopiowali / fragmentowali dokumentów. Ale może jeśli więcej osób zapyta, mógłbym coś dodać. Jest to również CW, więc możesz też zaproponować edycję
Merlyn Morgan-Graham,
3

A może ta drobna korekta?

@echo off
ECHO Quitting...
CALL :QUIT
:: The QUIT subroutine might have set the error code so let's take a look.
IF ERRORLEVEL 1 GOTO :EOF
ECHO Still here!
GOTO END

:QUIT
EXIT /B 1

:END
EXIT /B 0

Wynik:

Quitting...

Technicznie nie wychodzi to z podprogramu. Raczej po prostu sprawdza wynik podprogramu i podejmuje działania od tego momentu.

JMD
źródło
2
Dzięki, to z pewnością wykona zadanie, a jeśli nie będę w stanie znaleźć lepszej odpowiedzi, właśnie to będę musiał zrobić. Wolałbym jednak nie wklejać tego wiersza po każdym WYWOŁANIU w moim długim i złożonym pliku wsadowym.
Brown,
1

Jeśli nie chcesz wracać z procedury, nie używaj call: zamiast tego użyj goto.

@echo off
ECHO Quitting...
GOTO :QUIT
ECHO Will never be there!
GOTO END

:QUIT
EXIT /B 1

:END
EXIT /B 0
dolmen
źródło
Chodzi o to, jak to zrobić z podprogramów (np. Za pomocą wywołania), aby na to nie odpowiedzieć.
Steve Crane,
1

W moich plikach wsadowych umieściłem obsługę błędów. Możesz wywoływać programy obsługi błędów w następujący sposób:

CALL :WARNING "This is" "an important" "warning."

A oto koniec pliku wsadowego:

::-------------------------------------------------------------------
::  Decisions
::-------------------------------------------------------------------
:INFO
IF "_DEBUG"=="true" (
  ECHO INFO: %~1
  IF NOT "%~2"=="" ECHO          %~2
  IF NOT "%~3"=="" ECHO          %~3
)
EXIT /B 0
:WARNING
ECHO WARNING: %~1
IF NOT "%~2"=="" ECHO          %~2
IF NOT "%~3"=="" ECHO          %~3
EXIT /B 0
:FAILURE
ECHO FAILURE: %~1
IF NOT "%~2"=="" ECHO          %~2
IF NOT "%~3"=="" ECHO          %~3
pause>nul
:END
ECHO Closing Server.bat script
FOR /l %%a in (5,-1,1) do (TITLE %TITLETEXT% -- closing in %%as&PING.exe -n 2 -w 1 127.0.0.1>nul)
djangofan
źródło
1

Spowoduje to wyjście z bieżącego kontekstu i kontekstu nadrzędnego (tzn. Po wykonaniu w jednym callskrypcie głębokiego podprogramu nastąpi wyjście):

(goto) 2>nul || exit /b

Lub, jeśli potrzebujesz poziom błędu 0:

(goto) 2>nul || (
    type nul>nul
    exit /b
)

Zasadniczo (goto) 2>nulustawia poziom błędu na 1 (bez wyświetlania błędu), zwraca wykonanie w kontekście nadrzędnym i kod po wykonaniu podwójnego potoku w kontekście nadrzędnym. type nul>nulustawia poziom błędu na 0.

UPD:

Aby zwrócić wykonanie więcej niż dwa razy z rzędu, połącz kilka (goto) 2>nul ||takich:

(goto) 2>nul || (goto) 2>nul || (goto) 2>nul || (
    type nul>nul
    exit /b
)

Oto podprogram rekurencyjny, który zwraca kontekst wielokrotnie zmienną:

:Kill
(goto) 2>nul || (
    set /a depth=%1-1
    if %1 GEQ 1 (
        call:Kill !depth!
    )
    (goto) 2>nul || (type nul>nul)
)

Po wywołaniu z funkcji rekurencyjnej:

@echo off
setlocal EnableDelayedExpansion
call:Recurs 5
echo This won't be printed
exit /b

:Recurs
set /a ri+=1
echo %ri%
if %ri% LSS %1 (
    call:Recurs %1
)
echo This will be printed only once
call:Kill %1
exit /b

wyjście będzie:

1
2
3
4
5
This will be printed only once
BYCIE HYBRYDOWE
źródło