Esci dal file batch dalla subroutine


20

Come posso uscire da un file batch da una subroutine?

Se uso il comando EXIT, torno semplicemente alla riga in cui ho chiamato la subroutine e l'esecuzione continua.

Ecco un esempio:

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

:QUIT
EXIT /B 1

:END
EXIT /B 0

Produzione:

Quitting...
Still here!

Aggiornare:

Questa non è una risposta corretta, ma ho finito per fare qualcosa del tipo:

@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

L'istruzione double pipe di:

CALL :SUBROUTINE_WITH_ERROR || GOTO HANDLE_FAIL

è una scorciatoia per:

CALL :SUBROUTINE_WITH_ERROR 
IF ERRORLEVEL 1 GOTO HANDLE_FAIL    

Mi piacerebbe ancora sapere se c'è un modo per uscire direttamente da una subroutine piuttosto che dover fare in modo che CALLER gestisca la situazione, ma almeno questo fa il suo lavoro.


Aggiornamento n. 2: quando si chiama una subroutine da un'altra subroutine, chiamata nel modo sopra, chiamo dall'interno delle subroutine così:

CALL :SUBROUTINE_WITH_ERROR || EXIT /B 1

In questo modo, l'errore si propaga al "principale", per così dire. La parte principale del batch può quindi gestire l'errore con il gestore degli errori GOTO: FAILURE

Risposte:


21

Aggiungi questo nella parte superiore del file batch:

@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
)

Quindi puoi semplicemente chiamare:

  • EXIT [errorLevel] se si desidera uscire dall'intero file
  • EXIT /B [errorLevel] per uscire dalla subroutine corrente
  • GOTO :EOF per uscire dalla subroutine corrente

+1 per la menzione effettivaGOTO :EOF
Afrazier

1
Molto bella. Ho fatto una piccola modifica, assegna %~0alla variabile anziché true: if not "%selfwrapped%"=="%~0" ( set selfwrapped=%~0 .... ). In questo modo puoi usare lo stesso trucco in più script batch che si chiamano l'un l'altro.
GolezTrol,

Questa è un'ottima soluzione Pensi che valga la pena modificarlo per spiegare come funziona? Mi ci è voluto un minuto per decomprimere tutto ciò e rendermi conto che in realtà sta solo chiamando il file batch ( %~0) con tutti gli argomenti ( %*) da un cmd.exe nidificato, e /sviene utilizzato per controllare il modo in cui l' %ComSpec%argomento gestisce le doppie virgolette attorno al chiamata.
Sean,

@Sean Trovo che la brevità sia più utile per la maggior parte delle persone. Non avevo richiesto più documenti da 7 anni da quando l'ho scritto, quindi non sembra essere molto richiesto. Penso anche che ci sia un certo valore per le persone che cercano le cose da sole e non copiano / frammentano documenti. Ma forse se qualche altra persona mi chiedesse di aggiungere qualcosa. È anche un CW, quindi potresti anche proporre una modifica
Merlyn Morgan-Graham,

3

Che ne dici di questo piccolo aggiustamento?

@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

Produzione:

Quitting...

Tecnicamente questo non esce dall'interno della subroutine. Piuttosto, controlla semplicemente il risultato della subroutine e agisce da lì.


2
Grazie, sicuramente farebbe il lavoro, e se non trovo una risposta migliore è quello che dovrò fare. Tuttavia, preferirei non dover incollare quella riga dopo ogni CHIAMATA nel mio file batch lungo e complesso.
Brown,

1

Se non si desidera tornare dalla procedura, non utilizzare call: utilizzare invece goto.

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

:QUIT
EXIT /B 1

:END
EXIT /B 0

Il punto della domanda è come farlo dalle subroutine (cioè usando la chiamata) in modo che questo non risponda.
Steve Crane,

1

Ho inserito la gestione degli errori nei miei file batch. È possibile chiamare gestori di errori in questo modo:

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

Ed ecco la fine del file batch:

::-------------------------------------------------------------------
::  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)

1

Questo uscirà dal contesto corrente e da un contesto genitore (cioè, quando eseguito all'interno di uno callscript di subroutine approfondito) uscirà:

(goto) 2>nul || exit /b

Oppure, se è necessario il livello di errore 0:

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

Fondamentalmente, (goto) 2>nulimposta il livello di errore su 1 (senza generare un errore), restituisce l'esecuzione al contesto principale e al codice dopo l'esecuzione della doppia pipe nel contesto principale. type nul>nulimposta il livello di errore su 0.

UPD:

Per restituire l'esecuzione più di due volte di seguito, concatenarne diverse in (goto) 2>nul ||questo modo:

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

Ecco una subroutine ricorsiva per restituire il contesto un numero variabile di volte:

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

Quando viene chiamato da una funzione ricorsiva:

@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

l'output sarà:

1
2
3
4
5
This will be printed only once
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.