Quitter le fichier batch du sous-programme

20

Comment puis-je quitter un fichier batch de l'intérieur d'un sous-programme?

Si j'utilise la commande EXIT, je reviens simplement à la ligne où j'ai appelé le sous-programme et l'exécution continue.

Voici un exemple:

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

:QUIT
EXIT /B 1

:END
EXIT /B 0

Production:

Quitting...
Still here!

Mise à jour:

Ce n'est pas une bonne réponse, mais j'ai fini par faire quelque chose comme:

@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

La déclaration à double tuyau de:

CALL :SUBROUTINE_WITH_ERROR || GOTO HANDLE_FAIL

est un raccourci pour:

CALL :SUBROUTINE_WITH_ERROR 
IF ERRORLEVEL 1 GOTO HANDLE_FAIL    

J'aimerais toujours savoir s'il existe un moyen de sortir directement d'un sous-programme plutôt que de devoir faire appel à l'appelant pour gérer la situation, mais cela fait au moins le travail.


Mise à jour # 2: Lorsque j'appelle un sous-programme à partir d'un autre sous-programme, appelé de la manière ci-dessus, j'appelle ainsi de l'intérieur des sous-programmes:

CALL :SUBROUTINE_WITH_ERROR || EXIT /B 1

De cette façon, l'erreur se propage de nouveau au "principal", pour ainsi dire. La partie principale du lot peut alors gérer l'erreur avec le gestionnaire d'erreurs GOTO: FAILURE

Brown
la source

Réponses:

21

Ajoutez ceci en haut de votre fichier 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
)

Ensuite, vous pouvez simplement appeler:

  • EXIT [errorLevel] si vous souhaitez quitter l'intégralité du fichier
  • EXIT /B [errorLevel] pour quitter le sous-programme actuel
  • GOTO :EOF pour quitter le sous-programme actuel
Merlyn Morgan-Graham
la source
+1 pour avoir mentionnéGOTO :EOF
afrazier
1
Très agréable. Je l' ai fait une petite modification, attribuer %~0à la variable au lieu de true: if not "%selfwrapped%"=="%~0" ( set selfwrapped=%~0 .... ). De cette façon, vous pouvez utiliser la même astuce dans plusieurs scripts batch qui s'appellent les uns les autres.
GolezTrol
C'est une excellente solution. Pensez-vous que cela vaut la peine d'être édité pour expliquer comment cela fonctionne? Il m'a fallu une minute pour décompresser tout cela et réaliser qu'il s'agit en fait d'appeler le fichier batch ( %~0) avec tous les arguments ( %*) à partir d'un cmd.exe imbriqué, et le /sest utilisé pour contrôler la façon dont l' %ComSpec%argument gère les guillemets doubles autour du appel.
Sean
@Sean, je trouve que la brièveté est plus utile pour la plupart des gens. Plus de documents n'avaient pas été demandés depuis 7 ans que je l'ai écrit, donc il ne semble pas être très demandé. Je pense également qu'il y a une certaine valeur pour les gens qui recherchent eux-mêmes les choses et ne pas duper / fragmenter les documents. Mais peut-être que si quelques personnes supplémentaires me demandent, je pourrais ajouter quelque chose. C'est aussi une CW donc vous pouvez aussi proposer un montage
Merlyn Morgan-Graham
3

Que diriez-vous de cet ajustement mineur?

@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

Production:

Quitting...

Techniquement, cela ne sort pas du sous-programme. Au lieu de cela, il vérifie simplement le résultat du sous-programme et prend des mesures à partir de là.

JMD
la source
2
Merci, cela ferait certainement le travail, et si je ne trouve pas de meilleure réponse, c'est ce que je devrai faire. Cependant, je préfère ne pas avoir à coller cette ligne après chaque appel dans mon fichier de commandes long et complexe.
Brown
1

Si vous ne souhaitez pas revenir de la procédure, n'utilisez pas call: utilisez plutôt goto.

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

:QUIT
EXIT /B 1

:END
EXIT /B 0
dolmen
la source
Le point de la question est de savoir comment le faire à partir de sous-programmes (c'est-à-dire en utilisant l'appel) afin que cela ne réponde pas.
Steve Crane
1

J'ai mis la gestion des erreurs dans mes fichiers batch. Vous pouvez appeler des gestionnaires d'erreurs comme ceci:

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

Et voici la fin du fichier 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)
djangofan
la source
1

Cela quittera le contexte actuel et un contexte parent (c.-à-d. Lorsqu'il sera exécuté à l'intérieur d'un callscript de sous-programme profond se fermera):

(goto) 2>nul || exit /b

Ou, si vous avez besoin du niveau d'erreur 0:

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

Fondamentalement, (goto) 2>nuldéfinit errorlevel à 1 (sans générer d'erreur), renvoie l'exécution au contexte parent et au code après l'exécution du double canal dans le contexte parent. type nul>nuldéfinit errorlevel à 0.

UPD:

Pour renvoyer l'exécution plus de deux fois de suite, enchaînez plusieurs (goto) 2>nul ||comme ceci:

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

Voici un sous-programme récursif pour renvoyer le contexte un nombre variable de fois:

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

Lorsqu'il est appelé à partir d'une fonction récursive:

@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

la sortie sera:

1
2
3
4
5
This will be printed only once
HYBRID BEING
la source