Làm cách nào để tạo một tệp bó chấm dứt khi gặp lỗi?


290

Tôi có một tệp bó gọi các tệp thực thi giống nhau với các tham số khác nhau. Làm cách nào để khiến nó chấm dứt ngay lập tức nếu một trong các cuộc gọi trả về mã lỗi ở bất kỳ cấp độ nào?

Về cơ bản, tôi muốn tương đương với MSBuild ContinueOnError=false.


2
Lệnh shell nào sẽ chạy tập lệnh của bạn? Lệnh của DOS / Win9x.com hoặc cmd.exe của Win2k +? Vì điều đó tạo ra một thế giới khác biệt, bạn có thể vui lòng làm rõ điều đó trong bản chỉnh sửa câu hỏi của bạn không?
Mihai Limbășan

Câu trả lời:


299

Kiểm tra errorleveltrong một ifcâu lệnh và sau đó exit /b(chỉ thoát tệp b atch, không phải toàn bộ quá trình cmd.exe) cho các giá trị khác 0.

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

Nếu bạn muốn giá trị của errorlevel lan truyền bên ngoài tệp bó của bạn

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

nhưng nếu điều này là bên trong fornó sẽ có một chút khó khăn. Bạn sẽ cần một cái gì đó giống như:

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

Chỉnh sửa: Bạn phải kiểm tra lỗi sau mỗi lệnh. Không có kiểu cấu trúc "on error goto" toàn cầu trong lô cmd.exe / lệnh.com. Tôi cũng đã cập nhật mã của mình cho mỗi CodeMonkey , mặc dù tôi chưa bao giờ gặp phải lỗi âm bản trong bất kỳ vụ hack hàng loạt nào trên XP hoặc Vista.


11
Có cách nào để nêu nó một lần cho toàn bộ tập tin không? "Lỗi goto" hoặc một cái gì đó tương tự?
Josh Kodroff

3
+1 cho kiểm tra errorlevel âm. Có một kịch bản âm thầm thất bại vì một kết quả tiêu cực.
phát hành

1
Cẩn thận: việc mở rộng đã được kích hoạt là TIÊU CHUẨN và cũng được yêu cầu cho một if / khác hoặc bất kỳ khối nào khác
MarcH

1
@ system-PAUSE có sự khác biệt nào giữa hai 'nếu' đầu tiên được hiển thị không?
đơn giản hóa

1
Mở rộng bị trễ bật / tắt hoặc mở rộng lệnh (bắt buộc neq) được bật / tắt không quan trọng khi sử dụng if not errorlevel 1 exit /Bnhư được giải thích bởi Microsoft trong bài viết hỗ trợ Sử dụng toán tử chuyển hướng lệnh và đầu ra trợ giúp khi chạy if /?trong cửa sổ cmd. Lỗi hiện tại (mã thoát) được giữ khi thoát xử lý tệp bó với exit /B. Lưu ý: exitvới tham số /Byêu cầu tiện ích mở rộng lệnh được kích hoạt, hãy xem GOTO: EOF trở về đâu?
Mofi

252

Thêm || goto :labelvào mỗi dòng, và sau đó xác định a :label.

Ví dụ: tạo tệp .cmd này:

@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%

Xem thêm câu hỏi về việc thoát khỏi chương trình con tập tin hàng loạt .


4
Đây là một thành ngữ rất phổ biến trong hầu hết các ngôn ngữ kịch bản lệnh shell và nó đọc rất tốt: "Làm cái này hoặc cái này nếu nó thất bại .."
Fowl

3
Tôi sử dụng SET để theo dõi thủ công số dòng:command || (SET ErrorLine=102 && goto :error)
SandRock

1
@MarcelValdezOrozco Có vẻ như đây là thứ ||được tạo ra ngay từ đầu. Có thể không phải là cụ thể, nhưng "thử, làm điều này có lỗi" như Fowl đã đề cập. Câu hỏi của tôi là điều này có hoạt động cho tất cả các mã thoát không khác không? Chỉ tích cực?
jpmc26

3
@ jpmc26 đúng vậy, hãy chứng minh điều đó với chính mình - cmd /k exit -1 && echo success || echo fail- bản in thất bại.
Fowl

2
Bạn thậm chí có thể tránh các nhãn bằng một cái gì đó nhưcommand || exit /b %errorlevel%
Julian Brodwall

94

Ngắn nhất:

command || exit /b

Nếu bạn cần, bạn có thể đặt mã thoát:

command || exit /b 666

Và bạn cũng có thể đăng nhập:

command || echo ERROR && exit /b

4
Liệu exit / b có tình cờ trả lại mã thoát thất bại ban đầu không?
Frank Schwieterman

5
@FrankSchwieterman, vâng, %ERRORLEVEL%không bị ảnh hưởng khi bạn gọi exit /b, vì vậy mã lỗi được chuyển tiếp
Benoit Blanchon

24

Một bản cập nhật nhỏ, bạn nên thay đổi các kiểm tra cho "if errorlevel 1" thành ...

IF %ERRORLEVEL% NEQ 0 

Điều này là do trên XP, bạn có thể nhận được các số âm là lỗi. 0 = không có vấn đề, bất cứ điều gì khác là một vấn đề.

Và hãy ghi nhớ cách DOS xử lý các bài kiểm tra "IF ERRORLEVEL". Nó sẽ trả về true nếu số bạn đang kiểm tra là số đó hoặc cao hơn, vì vậy nếu bạn đang tìm kiếm các số lỗi cụ thể, bạn cần bắt đầu với 255 và làm việc xuống.


1
Không có lệnh bên trong và bên ngoài tiêu chuẩn nào của Windows thoát với giá trị âm. Microsoft cảnh báo bất kỳ người viết chương trình nào thoát ra với giá trị âm, ví dụ như trên bài viết MSDN về Môi trường.ExitCode . Trong thực tế, phải luôn luôn tìm ra mã thoát nào được sử dụng bởi một ứng dụng thành công và lỗi nào trong các lỗi khác nhau, xem HTML Help Workshop trả về lỗi sau khi tệp .chm được biên dịch thành công cho ví dụ MS tiêu cực về mong đợi của người dùng.
Mofi

17

Đây là một chương trình polyglot cho BASH và Windows CMD chạy một loạt các lệnh và thoát ra nếu bất kỳ lỗi nào trong số chúng bị lỗi:

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

Tôi đã sử dụng loại điều này trong quá khứ cho một kịch bản tích hợp liên tục nhiều nền tảng .


10

Tôi thích hình thức lệnh OR, vì tôi thấy chúng dễ đọc nhất (trái ngược với việc có if sau mỗi lệnh). Tuy nhiên, cách làm ngây thơ này, command || exit /b %ERRORLEVEL%sai .

Điều này là do lô mở rộng các biến khi một dòng được đọc lần đầu, thay vì khi chúng đang được sử dụng. Điều này có nghĩa là nếu commandtrong dòng trên không thành công, tệp bó sẽ thoát đúng, nhưng nó thoát với mã trả về 0, vì đó là giá trị của %ERRORLEVEL%ở đầu dòng. Rõ ràng, điều này là không mong muốn trong kịch bản của chúng tôi, vì vậy chúng tôi phải kích hoạt mở rộng bị trì hoãn , như vậy:

SETLOCAL EnableDelayedExpansion

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

Đoạn mã này sẽ thực thi các lệnh 1-4 và nếu bất kỳ lỗi nào trong số chúng bị lỗi, nó sẽ thoát với cùng mã thoát như lệnh thất bại đã làm.


1

Chúng tôi không thể luôn phụ thuộc vào ERRORLEVEL, vì nhiều lần các chương trình bên ngoài hoặc tập lệnh bó không trả về mã thoát.

Trong trường hợp đó, chúng ta có thể sử dụng kiểm tra chung cho các lỗi như thế này:

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

Và nếu chương trình xuất ra thứ gì đó để điều khiển, chúng ta cũng có thể kiểm tra 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)

1

Cho dù tôi đã cố gắng như thế nào, errorlevel luôn ở mức 0 ngay cả khi msbuild không thành công. Vì vậy, tôi đã xây dựng cách giải quyết của mình:

Xây dựng dự án và lưu nhật ký vào Build.log

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

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

tìm kiếm chuỗi "0 Error" trong nhật ký xây dựng, đặt kết quả thành var

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

lấy ký tự cuối cùng, cho biết có bao nhiêu dòng chứa chuỗi tìm kiếm

set result=%var:~-1%

echo "%result%"

nếu không tìm thấy chuỗi, thì lỗi> 0, xây dựng thất bại

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

Giải pháp đó được lấy cảm hứng từ bài đăng của Mechaflash tại Cách đặt đầu ra lệnh dưới dạng một biến trong tệp bó

https://ss64.com/nt/syntax-subopes.html


1
chỉ hoạt động cho số lss hơn mười. Tốt hơn; for /f %%F in ('type build.log^|find /c /i "0 Error") do set result=%%F. Lưu ý: find "0 Error"cũng sẽ tìm thấy 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
Vui lòng thêm thông tin vào câu trả lời của bạn. Chỉ là một khối mã không hữu ích lắm.
PoweredByOrange

Bên cạnh việc không thêm bất kỳ nhận xét nào, đoạn trích của bạn không giống như một ứng cử viên tốt để giải thích chức năng: nó dường như chứa rất nhiều điều hoàn toàn không liên quan đến câu hỏi.
Raúl Salinas-Monteagudo
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.