Como faço para que um arquivo em lotes seja finalizado ao encontrar um erro?

290

Eu tenho um arquivo em lotes que está chamando o mesmo executável repetidamente com parâmetros diferentes. Como faço para terminar imediatamente se uma das chamadas retornar um código de erro de qualquer nível?

Basicamente, quero o equivalente ao MSBuild ContinueOnError=false.

Josh Kodroff
fonte
2
Qual shell de comando estará executando seu script? Command.com do DOS / Win9x ou cmd.exe do Win2k +? Como isso faz muita diferença, você poderia esclarecer isso em uma edição da sua pergunta?
Mihai Limbășan 09/04/09

Respostas:

299

Verifique a errorlevelem uma ifdeclaração, e depois exit /b(sair do b arquivo atch apenas, não o todo o processo cmd.exe) para outros fins que 0 valores.

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

Se você deseja que o valor do nível de erro seja propagado para fora do seu arquivo em lotes

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

mas se isso estiver dentro de um for, fica um pouco complicado. Você precisará de algo mais 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: você deve verificar o erro após cada comando. Não há tipo global de construção "em erro," no lote cmd.exe / command.com. Também atualizei meu código pelo CodeMonkey , embora nunca tenha encontrado um nível de erro negativo em nenhum dos meus hackers em lote no XP ou Vista.

PAUSE do sistema
fonte
11
Existe uma maneira de indicá-lo uma vez para o arquivo inteiro? "On error goto" ou algo semelhante?
Josh Kodroff
3
+1 para a verificação do nível de erro negativo. Um script falhou silenciosamente devido a um resultado negativo.
devstuff
1
Cuidado: o enabledelayedexpansion é crítica e também necessário para um if / else ou qualquer outro bloco
Março
1
@ system-PAUSE Há alguma diferença entre os dois primeiros 'se' mostrados?
simpleuser 20/09/16
1
A expansão atrasada ativada / desativada ou as extensões de comando (necessárias para neq) ativadas / desativadas não importam no uso, if not errorlevel 1 exit /Bconforme explicado pela Microsoft no artigo de suporte Usando operadores de redirecionamento de comando e na saída de ajuda na execução if /?em uma janela cmd. O nível de erro atual (código de saída) é mantido ao sair do processamento do arquivo em lotes com exit /B. Nota: exitcom o parâmetro /Brequer extensões de comando ativadas, consulte Para onde o GOTO: EOF retorna?
Mofi
252

Adicione || goto :labela cada linha e defina a :label.

Por exemplo, crie este arquivo .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 também a pergunta sobre como sair da sub-rotina de arquivos em lote .

Galinha
fonte
4
É um estilo muito comum na maioria das linguagens de script shell, e ele lê bem: "Faça isso, ou isso se ele falhar .."
Fowl
3
Eu uso um SET para manter controlar manualmente o número da linha:command || (SET ErrorLine=102 && goto :error)
Sandrock
1
@MarcelValdezOrozco Parece-me que ||foi para isso que foi criado. Talvez não vá em particular, mas "tente, faça isso por erro", como Fowl mencionou. Minha pergunta é: isso funciona para todos os códigos de saída diferentes de zero? Apenas positivos?
precisa saber é o seguinte
3
@ jpmc26 sim, prove para si mesmo - cmd /k exit -1 && echo success || echo fail- as impressões falham.
Fowl
2
Você pode até evitar os rótulos com algo comocommand || exit /b %errorlevel%
Johannes Brodwall
94

O mais curto:

command || exit /b

Se precisar, você pode definir o código de saída:

command || exit /b 666

E você também pode registrar:

command || echo ERROR && exit /b
Benoit Blanchon
fonte
4
exit / b, por acaso, retorna o código de saída com falha original?
22815 Frank Schwieterman
5
@FrankSchwieterman, sim, %ERRORLEVEL%é intocável quando você chama exit /b, de modo que o código de erro é encaminhado
Benoit Blanchon
24

Uma atualização secundária, você deve alterar as verificações de "if errorlevel 1" para as seguintes ...

IF %ERRORLEVEL% NEQ 0 

Isso ocorre porque no XP você pode obter números negativos como erros. 0 = sem problemas, qualquer outra coisa é um problema.

E lembre-se da maneira como o DOS lida com os testes "SE ERRORLEVEL". Ele retornará verdadeiro se o número que você está verificando for esse número ou mais alto; portanto, se você estiver procurando por números de erro específicos, precisará começar com 255 e diminuir o trabalho.


fonte
1
Nenhum dos comandos internos e externos padrão do Windows sai sempre com um valor negativo. A Microsoft avisa qualquer gravador de programa para sair com um valor negativo, por exemplo, no artigo do MSDN sobre a propriedade Environment.ExitCode . Na verdade, é preciso sempre descobrir qual código de saída é usado por um aplicativo com êxito e quais os vários erros; consulte o HTML Help Workshop retorna um erro após o arquivo .chm compilado com êxito para obter um exemplo negativo da MS nas expectativas do usuário.
Mofi
17

Aqui está um programa poliglota para o BASH e o Windows CMD que executa uma série de comandos e sai se algum deles falhar:

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

Eu usei esse tipo de coisa no passado para um script de integração contínua de várias plataformas .

Erik Aronesty
fonte
10

Prefiro a forma de comando OR, pois as considero mais legíveis (em vez de ter um if após cada comando). No entanto, a maneira ingênua de fazer isso command || exit /b %ERRORLEVEL%está errada .

Isso ocorre porque o lote expande variáveis ​​quando uma linha é lida pela primeira vez, e não quando elas estão sendo usadas. Isso significa que, se a commandlinha acima falhar, o arquivo em lotes será encerrado corretamente, mas será encerrado com o código de retorno 0, porque é esse o valor de %ERRORLEVEL%estava no início da linha. Obviamente, isso é indesejável em nosso script, portanto, precisamos ativar a expansão atrasada , assim:

SETLOCAL EnableDelayedExpansion

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

Esse trecho executará os comandos de 1 a 4 e, se algum deles falhar, será encerrado com o mesmo código de saída que o comando com falha.

Xarn
fonte
1

Nem sempre podemos depender de ERRORLEVEL, porque muitas vezes programas externos ou scripts em lote não retornam códigos de saída.

Nesse caso, podemos usar verificações genéricas para falhas como esta:

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

E se o programa gerar algo para o console, podemos verificar também.

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

Não importa como tentei, o nível de erro sempre permanece 0, mesmo quando o msbuild falha. Então, eu criei minha solução alternativa:

Build Project e salve o log no Build.log

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

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

procure a string "0 Error" no log de construção, defina o resultado como var

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

obtém o último caractere, que indica quantas linhas contém a string de pesquisa

set result=%var:~-1%

echo "%result%"

se a sequência não for encontrada, erro> 0, falha na compilação

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

Essa solução foi inspirada na postagem de Mechaflash em Como definir comandos de saída como uma variável em um arquivo em lotes

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

E.Seven
fonte
1
funciona apenas para contagens de menos de dez. Melhor; for /f %%F in ('type build.log^|find /c /i "0 Error") do set result=%%F. Nota: find "0 Error"também encontrará 10 Errors.
5137 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
fonte
4
Adicione mais informações à sua resposta. Apenas um bloco de código não é muito útil.
precisa saber é o seguinte
Além de não ter adicionado nenhum comentário, seu snippet não parece um bom candidato para explicar uma funcionalidade: parece conter muitas coisas que são completamente irrelevantes para a pergunta.
Raúl Salinas-Monteagudo