Come faccio a far terminare un file batch dopo aver riscontrato un errore?


290

Ho un file batch che chiama ripetutamente lo stesso eseguibile con parametri diversi. Come posso farlo terminare immediatamente se una delle chiamate restituisce un codice di errore di qualsiasi livello?

Fondamentalmente, voglio l'equivalente di MSBuild ContinueOnError=false.


2
Quale shell di comando eseguirà il tuo script? Command.com di DOS / Win9x o cmd.exe di Win2k +? Dal momento che ciò fa la differenza, potresti chiarirlo in una modifica della tua domanda?
Mihai Limbășan,

Risposte:


299

Controllare errorlevelin ifun'istruzione, quindi exit /b(chiudere solo il file b atch, non l'intero processo cmd.exe) per valori diversi da 0.

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

Se si desidera che il valore del livello di errore si propaghi al di fuori del file batch

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

ma se è dentro a fordiventa un po 'complicato. Avrai bisogno di qualcosa di più simile a:

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

Modifica: è necessario controllare l'errore dopo ogni comando. Non esiste un tipo globale di costrutto "on error goto" nel batch cmd.exe / command.com. Ho anche aggiornato il mio codice per CodeMonkey , anche se non ho mai riscontrato un livello di errore negativo in nessuno dei miei hacking batch su XP o Vista.


11
C'è un modo per dirlo una volta per l'intero file? "On error goto" o qualcosa di simile?
Josh Kodroff,

3
+1 per il controllo del livello di errore negativo. Una sceneggiatura non ha funzionato silenziosamente a causa di un risultato negativo.
Devstuff,


1
@ system-PAUSE c'è qualche differenza tra i primi due 'if' mostrati?
simpleuser,

1
Espansione ritardata abilitata / disabilitata o le estensioni dei comandi (necessarie per neq) abilitate / disabilitate non hanno importanza sull'uso if not errorlevel 1 exit /Bcome spiegato da Microsoft nell'articolo di supporto Utilizzo degli operatori di reindirizzamento dei comandi e nell'output della guida if /?in esecuzione in una finestra cmd. Il livello di errore corrente (codice di uscita) viene mantenuto all'uscita dall'elaborazione del file batch con exit /B. Nota: exitcon il parametro /Brichiede le estensioni di comando abilitate, vedere Dove ritorna GOTO: EOF?
Mofi,

252

Aggiungi || goto :labela ciascuna riga, quindi definisci a :label.

Ad esempio, creare questo file .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%

Vedere anche la domanda sull'uscita dalla subroutine del file batch .


4
È un linguaggio molto comune nella maggior parte dei linguaggi di script di shell, e recita bene: "Fallo o questo se fallisce ..."
Fowl,

3
Uso un SET per tenere traccia manualmente del numero di riga:command || (SET ErrorLine=102 && goto :error)
SandRock

1
@MarcelValdezOrozco Mi sembra che questo sia ciò che è ||stato creato in primo luogo. Forse non andare in particolare, ma "prova, fallo in caso di errore", come menzionato da Fowl. La mia domanda è: funziona con tutti i codici di uscita diversi da zero? Solo positivi?
jpmc26,

3
@ jpmc26 sì, dimostralo a te stesso - cmd /k exit -1 && echo success || echo fail- le stampe falliscono.
Pollaio

2
Puoi persino evitare le etichette con qualcosa del generecommand || exit /b %errorlevel%
Johannes Brodwall,

94

Il più corto:

command || exit /b

Se necessario, è possibile impostare il codice di uscita:

command || exit /b 666

E puoi anche accedere:

command || echo ERROR && exit /b

4
exit / b restituisce per caso il codice di uscita originale non riuscito?
Frank Schwieterman,

5
@FrankSchwieterman, sì, %ERRORLEVEL%non viene toccato quando chiami exit /b, quindi il codice di errore viene inoltrato
Benoit Blanchon,

24

Un aggiornamento minore, è necessario modificare i controlli per "if errorlevel 1" nel seguente ...

IF %ERRORLEVEL% NEQ 0 

Questo perché su XP è possibile ottenere numeri negativi come errori. 0 = nessun problema, nient'altro è un problema.

E tieni presente il modo in cui DOS gestisce i test "IF ERRORLEVEL". Restituirà vero se il numero che stai cercando è quel numero o superiore, quindi se stai cercando numeri di errore specifici devi iniziare con 255 e andare giù.


1
Nessuno dei comandi interni ed esterni standard di Windows esce mai con un valore negativo. Microsoft avvisa qualsiasi autore di programmi di uscire con un valore negativo, ad esempio nell'articolo MSDN sulla proprietà Environment.ExitCode . In effetti, è necessario scoprire sempre quale codice di uscita viene utilizzato correttamente da un'applicazione e quali sono i vari errori, vedere HTML Help Workshop restituisce l'errore dopo aver compilato correttamente il file .chm per un esempio MS negativo sulle aspettative degli utenti.
Mofi,

17

Ecco un programma poliglotta per BASH e Windows CMD che esegue una serie di comandi ed esce in caso di errore:

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

Ho usato questo tipo di cose in passato per uno script di integrazione continua multipiattaforma .


10

Preferisco la forma di comando OR, poiché li trovo i più leggibili (piuttosto che avere un if dopo ogni comando). Tuttavia, il modo ingenuo di farlo, command || exit /b %ERRORLEVEL%è sbagliato .

Questo perché il batch espande le variabili alla prima lettura di una riga anziché quando vengono utilizzate. Ciò significa che se commandnella riga sopra fallisce, il file batch esce correttamente, ma esce con il codice di ritorno 0, perché questo è il valore di %ERRORLEVEL%all'inizio della riga. Ovviamente, questo è indesiderabile nel nostro script, quindi dobbiamo abilitare l'espansione ritardata , in questo modo:

SETLOCAL EnableDelayedExpansion

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

Questo frammento eseguirà i comandi 1-4 e, in caso di errore, uscirà con lo stesso codice di uscita del comando non riuscito.


1

Non possiamo sempre dipendere da ERRORLEVEL, perché molte volte programmi esterni o script batch non restituiscono codici di uscita.

In tal caso possiamo usare controlli generici per guasti come questo:

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

E se il programma emette qualcosa da console, possiamo anche controllarlo.

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)

1

Indipendentemente da come ho provato, il livello di errore rimane sempre 0 anche quando msbuild non è riuscito. Quindi ho costruito la mia soluzione alternativa:

Crea progetto e salva il registro in Build.log

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

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

cerca la stringa "0 Error" nel registro di build, imposta il risultato su var

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

ottiene l'ultimo carattere, che indica quante righe contengono la stringa di ricerca

set result=%var:~-1%

echo "%result%"

se la stringa non viene trovata, quindi errore> 0, compilazione non riuscita

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

Questa soluzione è stata ispirata dal post di Mechaflash su Come impostare l'output dei comandi come variabile in un file batch

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


1
funziona solo per conteggi lss di dieci. Meglio; for /f %%F in ('type build.log^|find /c /i "0 Error") do set result=%%F. Nota: find "0 Error"troverà anche 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

4
Aggiungi ulteriori informazioni alla tua risposta. Solo un blocco di codice non è molto utile.
PoweredByOrange

Oltre a non aver aggiunto alcun commento, il tuo frammento non sembra un buon candidato per spiegare una funzionalità: sembra contenere molte cose che sono completamente irrilevanti per la domanda.
Raúl Salinas-Monteagudo,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.