web-dev-qa-db-fra.com

Comment puis-je quitter un fichier de commandes à partir d'une fonction?

J'ai une fonction simple écrite pour vérifier les répertoires:

:direxist
if not exist %~1 (
    echo %~1 could not be found, check to make sure your location is correct.
    goto:end
    ) else (
    echo %~1 is a real directory
    goto:eof
    )

: end est écrit comme 

:end
endlocal

Je ne comprends pas pourquoi le programme ne s’arrêterait pas après l’appel de goto: end. J'ai une autre fonction qui utilise la même méthode pour arrêter le programme et cela fonctionne bien.

:PRINT_USAGE
echo Usage:
echo ------
echo <file usage information>
goto:end

Dans ce cas, le programme est arrêté après l'appel: end; pourquoi cela ne fonctionnerait-il pas dans: direxist? Merci de votre aide!

12
Brad Conyers

Je suppose que vous mélangez les déclarations call et goto ici. 

Une étiquette dans un fichier de commandes peut être utilisée avec une call ou une goto, mais le comportement est différent.
Si vous call une telle fonction _ elle retournera lorsque la fonction aura atteint la fin du fichier ou un exit /b ou goto :eof explicite (comme votre goto :end). 

Par conséquent, vous ne pouvez pas annuler votre lot si vous utilisez une étiquette en tant que fonction

Cependant, goto à une étiquette, ne sera pas retourné à l'appelant.

Utilisation d'une erreur synatx:

Mais il existe également un moyen de sortir du lot à partir d'un fonction.
Vous pouvez créer une erreur de syntaxe, cela oblige le lot à s’arrêter.
Mais il a pour effet secondaire que les variables locales (setlocal) ne seront pas supprimées.

@echo off
call :label hello
call :label stop
echo Never returns
exit /b

:label
echo %1
if "%1"=="stop" goto :halt
exit /b

:halt
call :haltHelper 2> nul

:haltHelper
() 
exit /b

Utilisation de CTRL-C:

La création d'un code d'erreur similaire au code d'erreur CTRL-C arrête également le traitement par lots.
Après la sortie, l'état setlocal est clean!
Voir la réponse de @ dbenham Quitter le script de traitement par lots depuis une fonction

Utilisation de la gestion avancée des exceptions:

Ce sont les solutions les plus puissantes, car il est capable de supprimer une quantité arbitraire de niveaux de pile, il peut être utilisé pour ne sortir que du fichier de traitement par lots actuel et également pour afficher le suivi de la pile.
Il utilise le fait que (goto), sans arguments, supprime un élément de la pile.
Voir Le traitement par lots de Windows prend-il en charge la gestion des exceptions?

15
jeb

la solution de Jeb fonctionne très bien. Mais cela peut ne pas être approprié dans toutes les circonstances. Il présente 2 inconvénients potentiels:

1) L’erreur de syntaxe interrompt le traitement par lots all. Ainsi, si un script batch appelle votre script et que celui-ci est arrêté avec l'erreur de syntaxe, le contrôle n'est pas renvoyé à l'appelant. Cela pourrait être mauvais.

2) Normalement, il existe un ENDLOCAL implicite pour chaque SETLOCAL à la fin du traitement par lot. Mais l'erreur de syntaxe fatale met fin au traitement par lots sans le caractère implicite ENDLOCAL! Cela peut avoir des conséquences désagréables :-( Voir mon post DosTips/ SETLOCAL continue après la résiliation du lot! pour plus d'informations.

Mise à jour 2015-03-20 _ ​​Voir https://stackoverflow.com/a/25474648/1012053 pour un moyen propre de mettre immédiatement fin au traitement par lots.

L’autre moyen d’interrompre un fichier de commandes dans une fonction consiste à utiliser la commande EXIT, qui permet de quitter complètement la commande Shell. Mais un peu d’utilisation créative de CMD peut le rendre utile pour résoudre le problème.

@echo off
if "%~1" equ "_GO_" goto :main
cmd /c ^""%~f0" _GO_ %*^"
exit /b

:main
call :label hello
call :label stop
echo Never returns
exit /b

:label
echo %1
if "%1"=="stop" exit
exit /b

J'ai à la fois ma version nommée "daveExit.bat" et la version de jeb nommée "jebExit.bat" sur mon PC.

Je les teste ensuite avec ce script batch

@echo off
echo before calling %1
call %1
echo returned from %1

Et voici les résultats

>test jebExit
before calling jebExit
hello
stop

>test daveExit
before calling daveExit
hello
stop
returned from daveExit

>

Un inconvénient potentiel de la solution EXIT est que les modifications apportées à l'environnement ne sont pas préservées. Cela peut être partiellement résolu en écrivant l'environnement dans un fichier temporaire avant de quitter, puis en le relisant.

@echo off
if "%~1" equ "_GO_" goto :main
cmd /c ^""%~f0" _GO_ %*^"
for /f "eol== delims=" %%A in (env.tmp) do set %%A
del env.tmp
exit /b

:main
call :label hello
set junk=saved
call :label stop
echo Never returns
exit /b

:label
echo %1
if "%1"=="stop" goto :saveEnvAndExit
exit /b

:saveEnvAndExit
set >env.tmp
exit

Mais les variables avec un caractère de nouvelle ligne (0x0A) dans la valeur ne seront pas conservées correctement.

5
dbenham

Si vous utilisez exit /b X pour quitter la fonction, la valeur ERRORLEVEL sera définie sur X. Vous pouvez ensuite utiliser le symbole ||conditionnel de traitement pour exécuter une commande si ERRORLEVEL est différent de zéro.

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

myfunction: Everything is good.
myfunction: got a FAIL. Will exit.
2
GrahamS

Voici ma solution qui supportera les routines imbriquées si toutes sont vérifiées pour errorlevel J'ajoute le test d'errolevel à tous mes appels (internes ou externes).

@echo off
call :error message&if errorlevel 1 exit /b %errorlevel%<
@echo continuing
exit /b 0
:error
@echo in %0
@echo message: %1
set yes=
set /p yes=[no]^|yes to continue
if /i "%yes%" == "yes" exit /b 0
exit /b 1
1
Charles Godwin