¿Cómo puedo hacer que un archivo por lotes termine al encontrar un error?

Tengo un archivo por lotes que llama al mismo ejecutable una y otra vez con diferentes parámetros. ¿Cómo hago que termine inmediatamente si una de las llamadas devuelve un código de error de cualquier nivel?

Básicamente, quiero el equivalente a ContinueOnError=false de MSBuild.

Compruebe el nivel de errorlevel en una instrucción if y luego exit /b (salga solo del archivo batch, no de todo el proceso cmd.exe) para valores distintos de 0.

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

Si desea que el valor del errorlevel se propague fuera de su archivo por lotes

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

pero si esto está dentro de for se vuelve un poco complicado. Necesitarás algo más como:

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

Editar: debe verificar el error después de cada comando. No existe un tipo de construcción global “on error goto” en el lote cmd.exe / command.com. También actualicé mi código por CodeMonkey , aunque nunca he encontrado un nivel de error negativo en ninguno de mis hackers en XP o Vista.

Añadir || goto :label || goto :label a cada línea, y luego defina a :label .

Por ejemplo, crea este archivo .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% 

Consulte también la pregunta sobre salir de la subrutina del archivo por lotes .

El más corto:

 command || exit /b 

Si lo necesita, puede establecer el código de salida:

 command || exit /b 666 

Y también puedes iniciar sesión:

 command || echo ERROR && exit /b 

Una actualización menor, debe cambiar las verificaciones de “if errorlevel 1” por las siguientes …

 IF %ERRORLEVEL% NEQ 0 

Esto se debe a que en XP puede obtener números negativos como errores. 0 = sin problemas, cualquier otra cosa es un problema.

Y tenga en cuenta la forma en que DOS maneja las pruebas “IF ERRORLEVEL”. Volverá a ser verdadero si el número que está buscando es ese número o más alto, de modo que si está buscando números de error específicos, debe comenzar con 255 y disminuir.

Aquí hay un progtwig políglota para BASH y Windows CMD que ejecuta una serie de comandos y se cierra si alguno de ellos falla:

 #!/bin/bash 2> nul :; set -o errexit :; function goto() { return $?; } command 1 || goto :error command 2 || goto :error command 3 || goto :error :error :; exit 0 exit /b 0 :error exit /b %errorlevel% 

He utilizado este tipo de cosas en el pasado para una secuencia de comandos de integración continua de múltiples plataformas.

No siempre podemos depender de ERRORLEVEL, porque muchas veces los progtwigs externos o los scripts por lotes no devuelven los códigos de salida.

En ese caso, podemos usar verificaciones genéricas para fallas como esta:

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

Y si el progtwig genera algo para la consola, podemos verificarlo también.

 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) 

Prefiero la forma de comando OR, ya que los encuentro más legibles (en lugar de tener un if después de cada comando). Sin embargo, la forma ingenua de hacer esto, command || exit /b %ERRORLEVEL% command || exit /b %ERRORLEVEL% es incorrecto .

Esto se debe a que el lote expande las variables cuando se lee por primera vez una línea, en lugar de cuando se utilizan. Esto significa que si falla el command en la línea anterior, el archivo por lotes sale correctamente, pero sale con el código de retorno 0, porque ese es el valor de %ERRORLEVEL% al comienzo de la línea. Obviamente, esto no es deseable en nuestro script, por lo que debemos habilitar la expansión retrasada , así:

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

Este fragmento ejecutará los comandos 1-4, y si alguno de ellos falla, saldrá con el mismo código de salida que el comando que falló.

No importa cuánto lo intenté, el nivel de error siempre permanece 0 incluso cuando msbuild falló. Así que construí mi solución:

Crear proyecto y guardar el registro en Build.log

 SET Build_Opt=/flp:summary;logfile=Build.log;append=true msbuild "myproj.csproj" /t:rebuild /p:Configuration=release /fl %Build_Opt% 

busque la cadena “0 Error” en el registro de comstackción, establezca el resultado en var

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

obtener el último carácter, que indica cuántas líneas contiene la cadena de búsqueda

 set result=%var:~-1% echo "%result%" 

si no se encuentra la cadena, error> 0, comstackción fallida

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

Esa solución se inspiró en la publicación de Mechaflash en Cómo configurar comandos de salida como una variable en un archivo por lotes

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

Simplemente use ERRORLEVEL para lograr el resultado que desee. Hay dos variables en el comando ERRORLEVEL que determinarían si un proceso falló o tuvo éxito, estas variables son ERRORLEVEL 0 y ERRORLEVEL 1 (1 para falla, 0 para aprobación). Aquí hay un ejemplo de cómo se usaría esto en un archivo por lotes:

 echo off cls ping localhost errorlevel 0 goto :good errorlevel 1 goto :bad :good cls echo Good! echo[ pause exit :bad cls echo Bad! echo[ pause exit 

Aquí hay un enlace con más información si lo necesita: Errorlevels

 @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