2010-07-12 6 views
14

J'ai un problème avec mon fichier batch. Il construit plusieurs programmes automatiquement en faisant quelque chose comme ceci:Quitter le script batch depuis l'intérieur d'une fonction

  • fixé des drapeaux de compilation
  • run 'gmake all'
  • appel de la fonction "vérifier le niveau d'erreur" et si errorlevel 1, sortie

il ressemble à ceci:

set FLAG=1 
... 
gmake all 
call :interactive_check 
set OTHERFLAG=1 
... 
gmake all 
call :interactive_check 

il y a 6 ou 7 d'entre eux (et i t pourrait grandir). J'ai donc fait une fonction pour vérifier errorlevel au lieu de copier/coller à chaque étape. Le problème est le suivant: le contrôle d'erreur est faite par une fonction:

:interactive_check 
if errorlevel 1 (
echo. 
echo /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\ 
echo Error in compilation process... exiting 
echo /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\ 
echo. 
cd %root_dir% 
exit /B 1 
) ELSE (
echo.Continuing to next step 
) 
goto:eof 

Maintenant, quand vous le lancez, le exit /B 1 sort simplement la fonction, mais pas le fichier batch. Savez-vous comment quitter le fichier batch complet sans avoir à copier/coller mon "if errorlevel 1 .." à chaque étape?

+2

Vous ne pouvez pas. Il suffit de remplacer 'call: interactive_check' par' if errorlevel 1 goto error'. Il n'y a pas beaucoup de différence entre copier-coller ancien ou dernier. :) – atzz

+0

Got it, résolu le problème avec cela et ça fonctionne bien, merci! – Gui13

Répondre

16

l'aide d'une erreur de syntaxe fatale, comme jeb démontre, ne tue tout le traitement par lots, mais il a aussi un effet secondaire désagréable - les changements de l'environnement après SETLOCAL sont conservés, même si ils sont censés être mis au rebut par ENDLOCAL implicite lorsque le traitement par lots se termine Voir mon article DosTips SETLOCAL continues after batch termination! pour plus d'informations. Basé sur les informations à Why does a non-interactive batch script think I've pressed control-C?, j'ai découvert un moyen propre de quitter tous les scripts batch à partir d'une routine ou d'un script CALLed, et tous les changements après SETLOCAL sont correctement rejetés.

@echo off 
setlocal 
set test=AFTER main SETLOCAL 
call :sub 
echo returning from main NEVER REACHED 
exit /b 

:sub 
setlocal 
set test=AFTER sub SETLOCAL 
set test 
call :ExitBatch 
echo returning from sub2 NEVER REACHED 
exit /b 


:ExitBatch - Cleanly exit batch processing, regardless how many CALLs 
if not exist "%temp%\ExitBatchYes.txt" call :buildYes 
call :CtrlC <"%temp%\ExitBatchYes.txt" 1>nul 2>&1 
:CtrlC 
cmd /c exit -1073741510 

:buildYes - Establish a Yes file for the language used by the OS 
pushd "%temp%" 
set "yes=" 
copy nul ExitBatchYes.txt >nul 
for /f "delims=(/ tokens=2" %%Y in (
    '"copy /-y nul ExitBatchYes.txt <nul"' 
) do if not defined yes set "yes=%%Y" 
echo %yes%>ExitBatchYes.txt 
popd 
exit /b 

Voici un exemple de sortie de test.bat ci-dessus. Vous pouvez voir que le script n'est jamais revenu de l'appel: ExitBatch et que la définition de la variable de test a été correctement supprimée une fois le traitement par lots terminé.

C:\test>test.bat 
test=AFTER sub SETLOCAL 

C:\test>set test 
Environment variable test not defined 

C:\test> 

La routine: ExitBatch peut être placée dans son propre ExitBatch.script de chauve-souris et placé quelque part dans votre PATH de sorte qu'il peut être commodément utilisé par n'importe quel script de lot.

@echo off 
:ExitBatch - Cleanly exit batch processing, regardless how many CALLs 
if not exist "%temp%\ExitBatchYes.txt" call :buildYes 
call :CtrlC <"%temp%\ExitBatchYes.txt" 1>nul 2>&1 
:CtrlC 
cmd /c exit -1073741510 

:buildYes - Establish a Yes file for the language used by the OS 
pushd "%temp%" 
set "yes=" 
copy nul ExitBatchYes.txt >nul 
for /f "delims=(/ tokens=2" %%Y in (
    '"copy /-y nul ExitBatchYes.txt <nul"' 
) do if not defined yes set "yes=%%Y" 
echo %yes%>ExitBatchYes.txt 
popd 
exit /b 

Mise à jour importante:

Il est maintenant possible de mettre en œuvre robuste exception manipulation avec lot pur. Non seulement vous pouvez lancer une exception qui peut terminer tous les traitements par lots, mais vous pouvez également attraper l'exception à un niveau supérieur, exécuter le code de nettoyage des exceptions spéciales et reprendre le traitement, ou continuer à quitter via un autre lancer! Voir Does Windows batch support exception handling? pour plus d'informations.

+0

Ingénieux. Je suis content que vous ayez pu mettre ma découverte au travail si rapidement; presque compense pour le déroutement qu'il m'a causé à l'origine. :-) –

+0

Eh bien, vous méritiez un accord, monsieur. – Gui13

+0

Belle solution, pour les cas normaux plus propres que les miens. – jeb

20

Pour une bonne solution voir la partie version améliorée

Vous pouvez arrêter un lot à tout moment, aussi à l'intérieur des appels de fonctions imbriquées.

Il vous suffit de créer une erreur de syntaxe, par exemple avec un bloc vide (), pour supprimer le message d'erreur, il peut être exécuté dans un appel, et le stderr de l'appel est redirigé vers NUL.

@echo off 
setlocal enabledelayedexpansion 

rem Do something 
call :interactive_check 

rem Do something 
call :interactive_check 

goto :eof 

:::::::::::::::::::::::::: 
:interactive_check 
if errorlevel 1 (
    echo. 
    echo /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\ 
    echo Error in compilation process... exiting 
    echo /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\ 
    call :halt 1 
) ELSE (
    echo.Continuing to next step 
) 
goto :eof 

:: Sets the errorlevel and stops the batch immediately 
:halt 
call :__SetErrorLevel %1 
call :__ErrorExit 2> nul 
goto :eof 

:__ErrorExit 
rem Creates a syntax error, stops immediately 
() 
goto :eof 

:__SetErrorLevel 
exit /b %time:~-2% 
goto :eof 

2017-04-09 Version améliorée: Sortie uniquement le lot en cours, mais pas le lot de l'appelant

Comme @dbenham mentionné, il y a une nouvelle technique pour la gestion des exceptions qui peuvent également être utilisés pour quitter uniquement le lot actuel.

@echo off 
echo Do something, detecting some fatal error 
call :ExitBatch 3 
exit /b 

:ExitBatch [errorcode] - Exits only the current batch file, regardless how many CALLs 
set _errLevel=%1 
REM *** Remove all calls from the current batch from the call stack 
:popStack 
(
    (goto) 2>nul 
    setlocal DisableDelayedExpansion  
    call set "caller=%%~0" 
    call set _caller=%%caller:~0,1%% 
    call set _caller=%%_caller::=%% 
    if not defined _caller (
     REM callType = func 
     rem set _errLevel=%_errLevel% 
     goto :popStack 
    ) 
    (goto) 2>nul 
    endlocal 
    cmd /c "exit /b %_errLevel%" 
) 
echo NEVER REACHED 
exit /b 
+0

Cool solution :) J'ai résolu le problème en utilisant la suggestion d'atzz, mais au cas où quelqu'un trébucherait ici, vous serez la réponse. – Gui13

+1

Cette solution ne conservera pas correctement les changements d'environnement après SETLOCAL. J'ai découvert une [solution propre] (http://stackoverflow.com/a/25474648/1012053) qui supprime correctement tous les changements après SETLOCAL. – dbenham

1

Je propose la solution suivante:

@echo off 

:: comment next line if you want to export any local variables in caller environment 
setlocal 

set FLAG=1 
rem Do something 
call :interactive_check 

set FLAG2=2 
rem Do something 
call :interactive_check 

goto :eof 

:interactive_check 
if errorlevel 1 (
    echo. 
    echo /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\ 
    echo Error in compilation process... exiting 
    echo /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\ 
    (goto) 2>nul & endlocal & exit /b %ERRORLEVEL% 
) else (
    echo Continuing to next step 
) 
goto :eof 

Il enregistre le code d'erreur de commande qui produisent une erreur. Si vous souhaitez enregistrer le code d'erreur à la valeur spécifique, puis modifiez la ligne:

(goto) 2>nul & endlocal & exit /b YOUR_EXITCODE_HERE 

Je ne solution aime pas avec l'erreur de syntaxe (voir jeb's post), car il termine également l'appelant de fichier de commandes en cours également.

1

Lorsque vous utilisez exit /b X pour quitter la fonction, elle définit ERRORLEVEL sur la valeur X. Vous pouvez ensuite utiliser le ||conditional processing symbol pour exécuter une autre commande si ERRORLEVEL est différent de call.

@echo off 
setlocal 
call :myfunction PASS || goto :eof 
call :myfunction FAIL || goto :eof 
echo Execution never gets here 
goto :eof 

:myfunction 
    if "%1"=="FAIL" ( 
     echo myfunction: got a FAIL. Will exit. 
     exit /b 1 
    ) 
    echo myfunction: Everything is good. 
    exit /b 0 

La sortie de ce script est la suivante:

myfunction: Everything is good. 
myfunction: got a FAIL. Will exit. 
Questions connexes