Comment puis-je terminer un fichier de commandes en cas d'erreur?

290

J'ai un fichier batch qui appelle le même exécutable encore et encore avec des paramètres différents. Comment puis-je le terminer immédiatement si l'un des appels renvoie un code d'erreur de n'importe quel niveau?

Fondamentalement, je veux l'équivalent de MSBuild ContinueOnError=false.

Josh Kodroff
la source
2
Quel shell de commande exécutera votre script? Command.com de DOS / Win9x ou cmd.exe de Win2k +? Étant donné que cela fait toute une différence, pourriez-vous clarifier cela dans une modification de votre question?
Mihai Limbășan

Réponses:

299

Vérifiez le errorleveldans une ifinstruction, puis exit /b(quittez le fichier b atch uniquement, pas l'intégralité du processus cmd.exe) pour les valeurs autres que 0.

same-executable-over-and-over.exe /with different "parameters"
if %errorlevel% neq 0 exit /b %errorlevel%

Si vous souhaitez que la valeur du niveau d'erreur se propage en dehors de votre fichier de commandes

if %errorlevel% neq 0 exit /b %errorlevel%

mais si c'est à l'intérieur d'un força devient un peu délicat. Vous aurez besoin de quelque chose de plus comme:

setlocal enabledelayedexpansion
for %%f in (C:\Windows\*) do (
    same-executable-over-and-over.exe /with different "parameters"
    if !errorlevel! neq 0 exit /b !errorlevel!
)

Edit: Vous devez vérifier l'erreur après chaque commande. Il n'y a pas de type global "en cas d'erreur" dans le lot cmd.exe / command.com. J'ai également mis à jour mon code par CodeMonkey , bien que je n'ai jamais rencontré de niveau d'erreur négatif dans aucun de mes hacking par lots sur XP ou Vista.

système PAUSE
la source
11
Existe-t-il un moyen de l'indiquer une fois pour l'ensemble du fichier? "On error goto" ou quelque chose de similaire?
Josh Kodroff le
3
+1 pour la vérification du niveau d'erreur négatif. Un script a-t-il échoué en silence à cause d'un résultat négatif?
devstuff
1
Attention: l'extension enabledelayedexpansion est CRITIQUE et également requise pour un bloc if / else ou tout autre bloc
MarcH
1
@ system-PAUSE y a-t-il une différence entre les deux premiers «si» affichés?
simpleuser
1
L'extension retardée activée / désactivée ou les extensions de commande (requises pour neq) activées / désactivées n'ont pas d'importance lors de l'utilisation if not errorlevel 1 exit /Bcomme expliqué par Microsoft dans l'article de support Utilisation des opérateurs de redirection de commande et dans la sortie d'aide lors de l'exécution if /?dans une fenêtre cmd. Le niveau d'erreur actuel (code de sortie) est conservé à la fin du traitement du fichier de commandes avec exit /B. Remarque: exitavec le paramètre /Bnécessite des extensions de commande activées, voir Où GOTO: EOF revient-il?
Mofi
252

Ajoutez || goto :labelà chaque ligne, puis définissez un:label .

Par exemple, créez ce fichier .cmd:

@echo off

echo Starting very complicated batch file...
ping -invalid-arg || goto :error
echo OH noes, this shouldn't have succeeded.
goto :EOF

:error
echo Failed with error #%errorlevel%.
exit /b %errorlevel%

Voir également la question sur la sortie du sous-programme de fichier de commandes .

Volaille
la source
4
C'est un idiome très courant dans la plupart des langages de script shell, et il se lit bien: "Faites ceci, ou ceci en cas d'échec .."
Fowl
3
J'utilise un SET pour suivre manuellement le numéro de ligne:command || (SET ErrorLine=102 && goto :error)
SandRock
1
@MarcelValdezOrozco Il me semble que c'est ce qui a ||été créé en premier lieu. Peut-être pas en particulier, mais "essayez de le faire par erreur" comme l'a mentionné Fowl. Ma question est la suivante: cela fonctionne-t-il pour tous les codes de sortie non nuls? Positifs seulement?
jpmc26
3
@ jpmc26 oui, prouvez-le vous-même - cmd /k exit -1 && echo success || echo fail- les impressions échouent.
Fowl
2
Vous pouvez même éviter les étiquettes avec quelque chose commecommand || exit /b %errorlevel%
Johannes Brodwall
94

Le plus court:

command || exit /b

Si vous en avez besoin, vous pouvez définir le code de sortie:

command || exit /b 666

Et vous pouvez également vous connecter:

command || echo ERROR && exit /b
Benoit Blanchon
la source
4
exit / b renvoie-t-il par hasard le code de sortie original défaillant?
Frank Schwieterman
5
@FrankSchwieterman, oui, %ERRORLEVEL%n'est pas touché lorsque vous appelez exit /b, donc le code d'erreur est transmis
Benoit Blanchon
24

Une mise à jour mineure, vous devez changer les vérifications de "si errorlevel 1" à ce qui suit ...

IF %ERRORLEVEL% NEQ 0 

En effet, sur XP, vous pouvez obtenir des nombres négatifs comme des erreurs. 0 = aucun problème, tout le reste est un problème.

Et gardez à l'esprit la façon dont DOS gère les tests "IF ERRORLEVEL". Il retournera vrai si le nombre que vous recherchez est ce nombre ou plus, donc si vous recherchez des numéros d'erreur spécifiques, vous devez commencer par 255 et réduire.


la source
1
Aucune des commandes internes et externes standard de Windows ne sort jamais avec une valeur négative. Microsoft avertit tout écrivain de programme de quitter avec une valeur négative, par exemple dans un article MSDN sur la propriété Environment.ExitCode . En fait, il faut toujours savoir quel code de sortie est utilisé par une application en cas de succès et lequel sur les diverses erreurs, voir HTML Help Workshop renvoie une erreur après avoir correctement compilé le fichier .chm pour un exemple MS négatif sur les attentes des utilisateurs.
Mofi
17

Voici un programme polyglotte pour BASH et Windows CMD qui exécute une série de commandes et se ferme si l'une d'entre elles échoue:

#!/bin/bash 2> nul

:; set -o errexit
:; function goto() { return $?; }

command 1 || goto :error

command 2 || goto :error

command 3 || goto :error

:; exit 0
exit /b 0

:error
exit /b %errorlevel%

J'ai utilisé ce type de chose dans le passé pour un script d' intégration continue sur plusieurs plates-formes .

Erik Aronesty
la source
10

Je préfère la forme de commande OR, car je les trouve les plus lisibles (par opposition à avoir un if après chaque commande). Cependant, la manière naïve de le faire command || exit /b %ERRORLEVEL%est fausse .

Cela est dû au fait que le lot développe les variables lors de la première lecture d'une ligne plutôt que lors de leur utilisation. Cela signifie que si le commanddans la ligne ci-dessus échoue, le fichier de commandes se ferme correctement, mais il se termine avec le code retour 0, car c'est ce que la valeur de %ERRORLEVEL%était au début de la ligne. Évidemment, cela n'est pas souhaitable dans notre script, nous devons donc activer l' expansion retardée , comme ceci:

SETLOCAL EnableDelayedExpansion

command-1 || exit /b !ERRORLEVEL!
command-2 || exit /b !ERRORLEVEL!
command-3 || exit /b !ERRORLEVEL!
command-4 || exit /b !ERRORLEVEL!

Cet extrait de code exécutera les commandes 1 à 4, et si l'une d'entre elles échoue, elle se fermera avec le même code de sortie que la commande défaillante.

Xarn
la source
1

Nous ne pouvons pas toujours dépendre de ERRORLEVEL, car de nombreux programmes externes ou scripts batch ne renvoient pas de codes de sortie.

Dans ce cas, nous pouvons utiliser des vérifications génériques des échecs comme ceci:

IF EXIST %outfile% (DEL /F %outfile%)
CALL some_script.bat -o %outfile%
IF NOT EXIST %outfile%  (ECHO ERROR & EXIT /b)

Et si le programme sort quelque chose sur la console, nous pouvons également le vérifier.

some_program.exe 2>&1 | FIND "error message here" && (ECHO ERROR & EXIT /b)
some_program.exe 2>&1 | FIND "Done processing." || (ECHO ERROR & EXIT /b)
Amr Ali
la source
1

Peu importe comment j'ai essayé, le niveau d'erreur reste toujours 0 même lorsque msbuild a échoué. J'ai donc construit ma solution:

Générez le projet et enregistrez le journal dans Build.log

SET Build_Opt=/flp:summary;logfile=Build.log;append=true

msbuild "myproj.csproj" /t:rebuild /p:Configuration=release /fl %Build_Opt%

recherchez la chaîne "0 Error" dans le journal de génération, définissez le résultat sur var

FOR /F "tokens=* USEBACKQ" %%F IN (`find /c /i "0 Error" Build.log`) DO (
    SET var=%%F
)
echo %var%

obtenir le dernier caractère, qui indique combien de lignes contiennent la chaîne de recherche

set result=%var:~-1%

echo "%result%"

si chaîne introuvable, erreur> 0, échec de la construction

if "%result%"=="0" ( echo "build failed" )

Cette solution a été inspirée par la publication de Mechaflash sur Comment définir la sortie des commandes en tant que variable dans un fichier de commandes

et https://ss64.com/nt/syntax-substring.html

E.Seven
la source
1
ne fonctionne que pour les comptes lss de dix. Mieux; for /f %%F in ('type build.log^|find /c /i "0 Error") do set result=%%F. Remarque: find "0 Error"trouvera également 10 Errors.
Stephan
-2
@echo off

set startbuild=%TIME%

C:\WINDOWS\Microsoft.NET\Framework\v3.5\msbuild.exe c:\link.xml /flp1:logfile=c:\link\errors.log;errorsonly /flp2:logfile=c:\link\warnings.log;warningsonly || goto :error

copy c:\app_offline.htm "\\lawpccnweb01\d$\websites\OperationsLinkWeb\app_offline.htm"

del \\lawpccnweb01\d$\websites\OperationsLinkWeb\bin\ /Q

echo Start Copy: %TIME%

set copystart=%TIME%

xcopy C:\link\_PublishedWebsites\OperationsLink \\lawpccnweb01\d$\websites\OperationsLinkWeb\ /s /y /d

del \\lawpccnweb01\d$\websites\OperationsLinkWeb\app_offline.htm

echo Started Build: %startbuild%
echo Started Copy: %copystart%
echo Finished Copy: %TIME%

c:\link\warnings.log

:error

c:\link\errors.log
Demican
la source
4
Veuillez ajouter plus d'informations à votre réponse. Un simple bloc de code n'est pas très utile.
PoweredByOrange
En plus de n'avoir ajouté aucun commentaire, votre extrait de code ne semble pas être un bon candidat pour expliquer une fonctionnalité: il semble contenir beaucoup de choses qui ne sont absolument pas pertinentes pour la question.
Raúl Salinas-Monteagudo