Wie kann ich eine Batchdatei beenden, wenn ein Fehler auftritt?

290

Ich habe eine Batch-Datei, die dieselbe ausführbare Datei immer wieder mit unterschiedlichen Parametern aufruft. Wie kann ich es sofort beenden, wenn einer der Aufrufe einen Fehlercode einer beliebigen Ebene zurückgibt?

Grundsätzlich möchte ich das Äquivalent von MSBuild ContinueOnError=false.

Josh Kodroff
quelle
2
Welche Befehlsshell führt Ihr Skript aus? Command.com von DOS / Win9x oder cmd.exe von Win2k +? Könnten Sie das bitte in einer Bearbeitung Ihrer Frage klarstellen, da dies einen großen Unterschied macht?
Mihai Limbășan

Antworten:

299

Überprüfen Sie die Anweisung errorlevelin einer ifAnweisung und suchen Sie dann exit /b( beenden Sie nur die B atch-Datei, nicht den gesamten Prozess cmd.exe) auf andere Werte als 0.

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

Wenn Sie möchten, dass sich der Wert der Fehlerstufe außerhalb Ihrer Batchdatei ausbreitet

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

aber wenn dies in einem ist, wird fores ein bisschen schwierig. Sie benötigen etwas mehr wie:

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

Bearbeiten: Sie müssen den Fehler nach jedem Befehl überprüfen. Es gibt kein globales Konstrukt vom Typ "on error goto" im Stapel "cmd.exe / command.com". Ich habe meinen Code auch per CodeMonkey aktualisiert , obwohl ich bei keinem meiner Batch-Hacking-Vorgänge unter XP oder Vista einen negativen Fehler festgestellt habe.

System PAUSE
quelle
11
Gibt es eine Möglichkeit, es einmal für die gesamte Datei anzugeben? "On error goto" oder ähnliches?
Josh Kodroff
3
+1 für die negative Fehlerprüfung. Hatte ein Skript stillschweigend wegen eines negativen Ergebnisses fehlgeschlagen.
Devstuff
1
Achtung: Die aktivierte Verzögerung der Erweiterung ist KRITISCH und auch für ein if / else oder einen anderen Block
erforderlich
1
@ system-PAUSE Gibt es einen Unterschied zwischen den ersten beiden angezeigten 'if'?
simpleuser
1
Verzögerte Erweiterung aktiviert / deaktiviert oder Befehlserweiterungen (erforderlich für neq) aktiviert / deaktiviert spielen keine Rolle bei der Verwendung, if not errorlevel 1 exit /Bwie von Microsoft im Support-Artikel Verwenden von Befehlsumleitungsoperatoren und in der Hilfeausgabe beim Ausführen if /?in einem Cmd-Fenster erläutert . Die aktuelle Fehlerstufe (Exit-Code) wird beim Beenden der Verarbeitung der Batch-Datei mit beibehalten exit /B. Hinweis: Wenn für denexit Parameter /Baktivierte Befehlserweiterungen erforderlich sind, siehe Wohin kehrt GOTO: EOF zurück?
Mofi
252

Fügen Sie || goto :labelzu jeder Zeile hinzu und definieren Sie dann a :label.

Erstellen Sie beispielsweise diese CMD-Datei:

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

Siehe auch Frage zum Beenden der Batchdatei-Subroutine .

Geflügel
quelle
4
Es ist eine sehr verbreitete Redewendung in den meisten Shell-Skriptsprachen und liest sich gut: "Tun Sie dies oder dies, wenn es fehlschlägt."
Fowl
3
Ich benutze ein SET, um die Zeilennummer manuell zu verfolgen:command || (SET ErrorLine=102 && goto :error)
SandRock
1
@MarcelValdezOrozco Mir scheint, dass dies das ist, wofür ||es überhaupt geschaffen wurde. Vielleicht nicht besonders, aber "versuchen Sie es mit einem Fehler", wie Fowl erwähnte. Meine Frage ist, ob dies für alle Exit-Codes ungleich Null funktioniert. Nur positiv?
jpmc26
3
@ jpmc26 ja das tut es, beweise es dir selbst - cmd /k exit -1 && echo success || echo fail- Drucke schlagen fehl.
Geflügel
2
Sie können sogar die Etiketten mit so etwas wiecommand || exit /b %errorlevel%
Johannes Brodwall
94

Der kürzeste:

command || exit /b

Bei Bedarf können Sie den Exit-Code festlegen:

command || exit /b 666

Und Sie können auch protokollieren:

command || echo ERROR && exit /b
Benoit Blanchon
quelle
4
Gibt exit / b zufällig den ursprünglich fehlgeschlagenen Exit-Code zurück?
Frank Schwieterman
5
@FrankSchwieterman, ja, %ERRORLEVEL%ist unberührt, wenn Sie anrufen exit /b, so dass der Fehlercode weitergeleitet wird
Benoit Blanchon
24

Bei einem kleinen Update sollten Sie die Überprüfungen für "if errorlevel 1" wie folgt ändern ...

IF %ERRORLEVEL% NEQ 0 

Dies liegt daran, dass Sie unter XP negative Zahlen als Fehler erhalten können. 0 = keine Probleme, alles andere ist ein Problem.

Und denken Sie daran, wie DOS die "IF ERRORLEVEL" -Tests behandelt. Es wird true zurückgegeben, wenn die Nummer, nach der Sie suchen, diese Nummer oder höher ist. Wenn Sie also nach bestimmten Fehlernummern suchen, müssen Sie mit 255 beginnen und nach unten arbeiten.


quelle
1
Keiner der internen und externen Windows-Standardbefehle wird jemals mit einem negativen Wert beendet. Microsoft warnt jeden Programmschreiber, mit einem negativen Wert zu beenden, z. B. in einem MSDN-Artikel über die Environment.ExitCode-Eigenschaft . Tatsächlich muss immer herausgefunden werden, welcher Exit-Code von einer Anwendung bei Erfolg verwendet wird und welcher bei den verschiedenen Fehlern. In HTML Help Workshop wird nach erfolgreich kompilierter .chm-Datei ein Fehler zurückgegeben, der ein negatives MS-Beispiel zu den Benutzererwartungen enthält.
Mofi
17

Hier ist ein Polyglot-Programm für BASH und Windows CMD, das eine Reihe von Befehlen ausführt und beendet, wenn einer von ihnen fehlschlägt:

#!/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%

Ich habe diese Art von Dingen in der Vergangenheit für ein Skript zur kontinuierlichen Integration mehrerer Plattformen verwendet .

Erik Aronesty
quelle
10

Ich bevorzuge die ODER-Befehlsform, da ich sie am besten lesbar finde (im Gegensatz zu einem Wenn nach jedem Befehl). Die naive Art, dies zu tun, command || exit /b %ERRORLEVEL%ist jedoch falsch .

Dies liegt daran, dass Batch Variablen beim ersten Lesen einer Zeile erweitert und nicht, wenn sie verwendet werden. Dies bedeutet, dass, wenn das commandin der obigen Zeile fehlschlägt, die Batchdatei ordnungsgemäß beendet wird, jedoch mit dem Rückkehrcode 0 beendet wird, da der Wert von %ERRORLEVEL%am Anfang der Zeile stand. Offensichtlich ist dies in unserem Skript unerwünscht, daher müssen wir die verzögerte Erweiterung wie folgt aktivieren :

SETLOCAL EnableDelayedExpansion

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

Dieses Snippet führt die Befehle 1 bis 4 aus. Wenn einer von ihnen fehlschlägt, wird es mit demselben Exit-Code beendet wie der fehlgeschlagene Befehl.

Xarn
quelle
1

Wir können uns nicht immer auf ERRORLEVEL verlassen, da externe Programme oder Batch-Skripte häufig keine Exit-Codes zurückgeben.

In diesem Fall können wir generische Überprüfungen für Fehler wie diesen verwenden:

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

Und wenn das Programm etwas an die Konsole ausgibt, können wir es auch überprüfen.

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
quelle
1

Egal wie ich es versucht habe, die Fehlerstufe bleibt immer 0, auch wenn msbuild fehlgeschlagen ist. Also habe ich meine Problemumgehung erstellt:

Projekt erstellen und Protokoll in Build.log speichern

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

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

Suchen Sie im Build-Protokoll nach der Zeichenfolge "0 Error" und setzen Sie das Ergebnis auf var

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

Holen Sie sich das letzte Zeichen, das angibt, wie viele Zeilen die Suchzeichenfolge enthalten

set result=%var:~-1%

echo "%result%"

Wenn die Zeichenfolge nicht gefunden wurde, ist der Fehler> 0, Build fehlgeschlagen

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

Diese Lösung wurde von Mechaflashs Beitrag unter So legen Sie Befehle fest, die als Variable in einer Batchdatei ausgegeben werden

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

E.Seven
quelle
1
funktioniert nur für Zählungen von weniger als zehn. Besser; for /f %%F in ('type build.log^|find /c /i "0 Error") do set result=%%F. Hinweis: find "0 Error"wird auch finden 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
Demikaner
quelle
4
Bitte fügen Sie Ihrer Antwort weitere Informationen hinzu. Nur ein Codeblock ist nicht sehr hilfreich.
PoweredByOrange
Abgesehen davon, dass Sie keine Kommentare hinzugefügt haben, scheint Ihr Snippet kein guter Kandidat für die Erklärung einer Funktionalität zu sein: Es scheint viele Dinge zu enthalten, die für die Frage völlig irrelevant sind.
Raúl Salinas-Monteagudo