Jak mogę zakończyć plik wsadowy po napotkaniu błędu?


290

Mam plik wsadowy, który w kółko wywołuje ten sam plik wykonywalny z różnymi parametrami. Jak sprawić, by zakończyło się natychmiast, jeśli jedno z wywołań zwróci kod błędu dowolnego poziomu?

Zasadniczo chcę odpowiednik MSBuild ContinueOnError=false.


2
Jaka powłoka poleceń uruchomi twój skrypt? DOS / Win9x's command.com lub Win2k +'s cmd.exe? Skoro to robi różnicę, czy mógłbyś wyjaśnić to w edycji swojego pytania?
— Mihai Limbășan

Odpowiedzi:


299

Sprawdź, czy errorlevelw ifrachunku, a następnie exit /b(opuścić b plik Atch tylko nie cały proces cmd.exe) do wartości innych niż 0.

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

Jeśli chcesz, aby wartość poziomu błędu propagowała się poza plikiem wsadowym

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

ale jeśli to jest w środku for, staje się to trochę trudne. Potrzebujesz czegoś więcej:

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

Edycja: Musisz sprawdzić błąd po każdym poleceniu. W partii cmd.exe / command.com nie ma globalnego typu „na błąd goto”. Zaktualizowałem również mój kod według CodeMonkey , chociaż nigdy nie spotkałem się z ujemnym poziomem błędu w żadnym z moich hackowań wsadowych na XP lub Vistę.


11
Czy istnieje sposób, aby podać to raz dla całego pliku? „On error goto” czy coś podobnego?
— Josh Kodroff,

3
+1 za ujemne sprawdzenie poziomu błędu. Skrypt po cichu zawiódł z powodu wyniku negatywnego.
— devstuff

1
Ostrożnie: włączone opóźnione rozszerzenie jest KRYTYCZNE i wymagane również dla bloku if / else lub dowolnego innego bloku
— MarcH

1
@ system-PAUSE czy jest jakaś różnica między pierwszymi dwoma wyświetlanymi „jeśli”?
— simpleuser

1
Opóźnione rozszerzenie włączone / wyłączone lub rozszerzenia poleceń (wymagane dla neq) włączone / wyłączone nie ma znaczenia przy używaniu, if not errorlevel 1 exit /Bjak wyjaśniono w artykule pomocy technicznej Microsoft Korzystanie z operatorów przekierowywania poleceń i pomoc wyjściowa po uruchomieniu if /?w oknie cmd. Bieżący poziom błędu (kod wyjścia) jest utrzymywany przy wychodzeniu z przetwarzania pliku wsadowego za pomocą exit /B. Uwaga: exitjeśli parametr /Bwymaga włączonych rozszerzeń poleceń, zobacz Gdzie wraca GOTO: EOF?
— Mofi

252

Dodaj || goto :labeldo każdej linii, a następnie zdefiniuj :label.

Na przykład utwórz ten plik .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%

Zobacz także pytanie dotyczące wychodzenia z podprogramu pliku wsadowego .


4
Jest to bardzo powszechny idiom w większości języków skryptowych powłoki i brzmi dobrze: „Zrób to, albo jeśli to się nie powiedzie ...”
— Fowl

3
Używam SET, aby ręcznie śledzić numer linii:command || (SET ErrorLine=102 && goto :error)
— SandRock,

1
@MarcelValdezOrozco Wydaje mi się, że po to właśnie ||stworzono. Być może nie w szczególności, ale „spróbuj, zrób to z błędem”, jak wspomniała Fowl. Moje pytanie brzmi: czy to działa dla wszystkich niezerowych kodów wyjścia? Tylko pozytywne?
— jpmc26,

3
@ jpmc26 tak to robi, udowodnij to sobie - cmd /k exit -1 && echo success || echo fail- drukowanie kończy się niepowodzeniem.
— Ptactwo

2
Możesz nawet uniknąć etykiet z czymś takimcommand || exit /b %errorlevel%
— Johannes Brodwall

95

Najkrótszy:

command || exit /b

Jeśli potrzebujesz, możesz ustawić kod wyjścia:

command || exit /b 666

Możesz także zalogować:

command || echo ERROR && exit /b

4
czy exit / b przypadkowo zwraca pierwotny kod wyjścia, który się nie powiódł?
— Frank Schwieterman

5
@FrankSchwieterman, tak, %ERRORLEVEL%pozostaje nietknięty, kiedy dzwonisz exit /b, więc kod błędu jest przekazywany
— Benoit Blanchon

24

Jedna drobna aktualizacja, należy zmienić sprawdzanie „jeśli poziom błędu 1” na następujące ...

IF %ERRORLEVEL% NEQ 0 

Wynika to z faktu, że w XP można uzyskać liczby ujemne jako błędy. 0 = brak problemów, wszystko inne stanowi problem.

I pamiętaj o tym, jak DOS obsługuje testy „IF ERRORLEVEL”. Zwróci wartość true, jeśli liczba, którą sprawdzasz, jest tą liczbą lub wyższą, więc jeśli szukasz określonych numerów błędów, musisz zacząć od 255 i pracować dalej.


1
Żadne ze standardowych wewnętrznych i zewnętrznych poleceń systemu Windows nigdy nie wychodzi z wartością ujemną. Microsoft ostrzega każdego programisty, aby zakończył pracę z wartością ujemną, na przykład w artykule MSDN na temat właściwości Environment.ExitCode . W rzeczywistości zawsze trzeba dowiedzieć się, który kod zakończenia jest używany przez aplikację w przypadku powodzenia, a który w przypadku różnych błędów, zobacz Warsztat pomocy HTML zwraca błąd po pomyślnym skompilowaniu pliku .chm dla negatywnego przykładu MS na oczekiwania użytkownika.
— Mofi

17

Oto program polyglot dla BASH i Windows CMD, który uruchamia serię poleceń i kończy działanie, jeśli któreś z nich się nie powiedzie:

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

W przeszłości używałem tego typu rzeczy do tworzenia skryptów ciągłej integracji na wielu platformach .


10

Wolę formę OR, ponieważ uważam, że są one najbardziej czytelne (w przeciwieństwie do opcji if po każdym poleceniu). Jednak naiwny sposób robienia tego command || exit /b %ERRORLEVEL%jest niewłaściwy .

Wynika to z faktu, że partia rozszerza zmienne, gdy wiersz jest czytany po raz pierwszy, a nie kiedy są używane. Oznacza to, że jeśli commandpowyższy wiersz nie powiedzie się, plik wsadowy kończy się poprawnie, ale wychodzi z kodem powrotu 0, ponieważ taka była wartość %ERRORLEVEL%na początku wiersza. Jest to oczywiście niepożądane w naszym skrypcie, dlatego musimy włączyć opóźnioną ekspansję :

SETLOCAL EnableDelayedExpansion

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

Ten fragment kodu wykona polecenia 1-4, a jeśli którykolwiek z nich zawiedzie, zakończy działanie z tym samym kodem wyjścia, co polecenie nieudane.


1

Nie zawsze możemy polegać na ERRORLEVEL, ponieważ wiele razy programy zewnętrzne lub skrypty wsadowe nie zwracają kodów wyjścia.

W takim przypadku możemy zastosować ogólne kontrole pod kątem takich awarii:

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

A jeśli program wypisze coś na konsolę, możemy to również sprawdzić.

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

Bez względu na to, jak próbowałem, poziom błędu zawsze pozostaje równy 0, nawet jeśli msbuild nie powiódł się. Więc zbudowałem swoje obejście:

Utwórz projekt i zapisz dziennik w Build.log

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

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

wyszukaj ciąg „0 Error” w dzienniku kompilacji, ustaw wynik na var

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

pobierz ostatni znak, który wskazuje, ile wierszy zawiera szukany ciąg

set result=%var:~-1%

echo "%result%"

jeśli ciąg nie zostanie znaleziony, to błąd> 0, kompilacja nie powiodła się

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

To rozwiązanie zostało zainspirowane postem Mechaflasha na stronie Jak ustawić dane wyjściowe poleceń jako zmienne w pliku wsadowym

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


1
działa tylko dla liczby lss większej niż dziesięć. Lepszy; for /f %%F in ('type build.log^|find /c /i "0 Error") do set result=%%F. Uwaga: find "0 Error"również znajdzie 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
Dodaj więcej informacji do swojej odpowiedzi. Sam blok kodu nie jest zbyt pomocny.
— PoweredByOrange

Oprócz tego, że nie dodał żadnych komentarzy, fragment kodu nie wygląda na dobrego kandydata do wyjaśnienia funkcji: wydaje się, że zawiera wiele rzeczy, które są całkowicie nieistotne dla pytania.
— Raúl Salinas-Monteagudo
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.