Як зробити так, щоб пакетний файл закінчувався при виявленні помилки?


290

У мене є пакетний файл, який викликає один і той же виконуваний файл з різними параметрами. Як зробити так, щоб воно було негайно припинено, якщо один із викликів повертає код помилки будь-якого рівня?

В основному я хочу еквівалент MSBuild ContinueOnError=false.


2
Яка командна оболонка буде працювати з вашим сценарієм? DOS / Win9x's command.com або Win2k + 's cmd.exe? Оскільки це робить світ різницею, ви можете, будь ласка, уточнити це в редакції свого питання?
Mihai Limbășan

Відповіді:


299

Перевірте оператор errorlevelin if, а потім exit /b(вийдіть лише з файлу b atch, а не на весь процес cmd.exe) на значення, відмінні від 0.

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

Якщо ви хочете, щоб значення рівня помилок поширювалося поза вашим пакетним файлом

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

але якщо це всередині, forвоно стає дещо складним. Вам знадобиться щось більше на кшталт:

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

Редагувати: Ви повинні перевірити помилку після кожної команди. У пакеті cmd.exe / command.com не існує глобального типу "on error goto". Я також оновлював свій код на CodeMonkey , хоча ніколи не стикався з негативним рівнем помилок в жодному з моїх пакетних злому на XP або Vista.


11
Чи є спосіб констатувати його один раз для всього файлу? "Помилка goto" чи щось подібне?
Джош Кодрофф

3
+1 для перевірки рівня негативної помилки. Якщо сценарій мовчки вийшов з ладу через негативний результат.
devstuff

1
Будьте обережні: розширення enabledelayedex є критичним, а також необхідне для блоку if / else або будь-якого іншого блоку
MarcH

1
@ system-PAUSE Чи є різниця між першими двома показаними "якщо"?
simpleuser

1
Увімкнено / відключено розширення з увімкненим / вимкненим розширенням або розширення команд (необхідне для neq), if not errorlevel 1 exit /Bяк це пояснено Microsoft у статті підтримки, використовуючи оператори переадресації команд, та в довідці щодо запуску if /?у вікні cmd. Поточний рівень помилок (код виходу) зберігається при виході з обробки пакетного файлу за допомогою exit /B. Примітка: exitдля параметра /Bпотрібні активовані розширення команд, див. Де повертається GOTO: EOF?
Мофі

252

Додайте || goto :labelдо кожного рядка, а потім визначте a :label.

Наприклад, створіть цей .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%

Дивіться також питання про вихід підпрограми пакетного файлу .


4
Це дуже поширена ідіома в більшості мов скриптових сценаріїв, і вона добре читає: "Зробіть це, або це, якщо не вдасться .."
Фол

3
Я використовую SET, щоб відстежувати номер лінії вручну:command || (SET ErrorLine=102 && goto :error)
SandRock

1
@MarcelValdezOrozco Мені здається, що саме для цього ||створено. Можливо, не йти зокрема, але "спробуйте, зробіть це помилково", як згадував Фоул. Моє запитання: чи працює це для всіх ненульових кодів виходу? Позитивні лише?
jpmc26

3
@ jpmc26 так це, доведіть це собі - cmd /k exit -1 && echo success || echo fail- відбитки не вдається.
Птиця

2
Можна навіть уникнути етикетки з чимось на кшталтcommand || exit /b %errorlevel%
Йоганнес Бродволл

95

Найкоротший:

command || exit /b

Якщо вам потрібно, ви можете встановити код виходу:

command || exit /b 666

Ви також можете увійти:

command || echo ERROR && exit /b

4
повертає виїзд / б випадково вихідний код виходу з ладу?
Френк Швітерман

5
@FrankSchwieterman, так, %ERRORLEVEL%під час дзвінка недоторканий exit /b, тому код помилки пересилається
Benoit Blanchon

24

Одне незначне оновлення, ви повинні змінити чеки на "if errorlevel 1" на наступне ...

IF %ERRORLEVEL% NEQ 0 

Це тому, що на XP ви можете отримати негативні числа як помилки. 0 = немає проблем, все інше - це проблема.

І майте на увазі те, як DOS обробляє тести "ЯКЩО ПОМИЛКУВАТИ". Він повернеться істинним, якщо число, на яке ви перевіряєте, це число або вище, тому якщо ви шукаєте конкретні номери помилок, вам потрібно почати з 255 і працювати вниз.


1
Жодна із стандартних внутрішніх та зовнішніх команд Windows не виходить із негативним значенням. Microsoft попереджає будь-якого програмового програму вийти з негативним значенням, наприклад, у статті MSDN про властивість Environment.ExitCode . Насправді завжди слід з’ясувати, який код виходу використовується додатком для успіху, а який - про різні помилки, див. Довідку HTML Help Workshop повертає помилку після успішно складеного файлу .chm для негативного прикладу MS на очікування користувачів.
Мофі

17

Ось поліглот-програма для BASH та Windows CMD, яка виконує ряд команд і вимикає, якщо будь-яка з них виходить з ладу:

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

Раніше я використовував такий тип речей для сценарію безперервної інтеграції з декількома платформами .


10

Я віддаю перевагу команді OR АБО, оскільки я вважаю їх найбільш читабельними (на відміну від наявності if після кожної команди). Однак, наївний спосіб цього зробити command || exit /b %ERRORLEVEL%- неправильно .

Це відбувається тому, що пакет розширює змінні під час першого читання рядка, а не коли вони використовуються. Це означає, що якщо commandрядок у верхньому рядку не вдається, пакетний файл закінчується належним чином, але він виходить із кодом повернення 0, тому що саме це значення %ERRORLEVEL%було на початку рядка. Очевидно, що це в нашому сценарії небажано, тому ми повинні включити затримку розширення , як-от так:

SETLOCAL EnableDelayedExpansion

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

Цей фрагмент буде виконувати команди 1-4, і якщо будь-яка з них виходить з ладу, він вийде з тим же кодом виходу, що і команда, що відмовилась.


1

Ми не завжди можемо залежати від ERRORLEVEL, оскільки багато разів зовнішні програми чи пакетні скрипти не повертають вихідні коди.

У такому випадку ми можемо використовувати загальні перевірки на помилки на кшталт цього:

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

І якщо програма видає щось для консолі, ми також можемо це перевірити.

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

Як би я не намагався, рівень помилок завжди залишається 0, навіть коли msbuild не вдалося. Тому я побудував своє вирішення:

Створіть проект і збережіть журнал до Build.log

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

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

шукайте рядок "0 Помилка" у журналі збору, встановіть результат на var

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

отримати останній символ, який вказує, скільки рядків містить рядок пошуку

set result=%var:~-1%

echo "%result%"

якщо рядок не знайдено, то помилка> 0, збірка не вдалася

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

Це рішення було натхнене публікацією Мечафлаша в " Як встановити команди виведення у вигляді змінної в пакетному файлі

та https://ss64.com/nt/syntax-substring.html


1
працює лише для лічильника lss, ніж десяти. Краще; for /f %%F in ('type build.log^|find /c /i "0 Error") do set result=%%F. Примітка: find "0 Error"також знайдемо 10 Errors.
Стефан

-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
Будь ласка, додайте більше інформації до своєї відповіді. Просто блок коду не дуже корисний.
PoweredByOrange

Окрім того, що не додав жодних коментарів, ваш фрагмент не схожий на хорошого кандидата для пояснення функціональності: він, здається, містить багато речей, які абсолютно не мають значення для питання.
Рауль Салінас-Монтеагудо
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.