2010-07-12 37 views
14

Tôi gặp sự cố với tệp lô của mình. Nó xây dựng một số chương trình tự động bằng cách làm một cái gì đó như thế này:Thoát tập lệnh lô từ bên trong một hàm

  • thiết lập một số cờ biên soạn
  • chạy 'gmake tất cả'
  • gọi là "kiểm tra mức độ lỗi" chức năng và nếu errorlevel 1, lối

Vì vậy, nó trông như thế này:

set FLAG=1 
... 
gmake all 
call :interactive_check 
set OTHERFLAG=1 
... 
gmake all 
call :interactive_check 

Có 6 hoặc 7 trong số này (và tôi t có thể phát triển). Vì vậy, tôi đã thực hiện một chức năng để kiểm tra errorlevel thay vì sao chép/dán nó ở mọi bước. Vấn đề là thế này: việc kiểm tra lỗi được thực hiện thông qua một chức năng:

:interactive_check 
if errorlevel 1 (
echo. 
echo /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\ 
echo Error in compilation process... exiting 
echo /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\ 
echo. 
cd %root_dir% 
exit /B 1 
) ELSE (
echo.Continuing to next step 
) 
goto:eof 

Bây giờ, khi chạy nó, exit /B 1 chỉ đơn giản là ra khỏi chức năng, nhưng không phải là tập tin thực thi.

Bạn có biết cách thoát khỏi tệp hàng loạt hoàn chỉnh mà không phải sao chép/dán "if errorlevel 1 .." của mình vào mỗi bước không?

+2

Bạn không thể. Chỉ cần thay thế 'call: interactive_check' bằng' if errorlevel 1 goto error'. Không có nhiều sự khác biệt giữa sao chép hoặc dán cũ hoặc sau này. :) – atzz

+0

OK, giải quyết vấn đề với điều này và nó hoạt động tốt, cảm ơn! – Gui13

Trả lời

16

Sử dụng một lỗi cú pháp gây tử vong, như Jeb chứng minh, không giết tất cả xử lý hàng loạt, nhưng nó cũng có một tác dụng phụ khó chịu - thay đổi môi trường sau khi setlocal được bảo quản, mặc dù họ có nghĩa vụ phải được loại bỏ thông qua endlocal ngầm khi xử lý hàng loạt kết thúc. Xem DosTips của tôi đăng SETLOCAL continues after batch termination! để biết thêm thông tin.

Dựa trên thông tin tại Why does a non-interactive batch script think I've pressed control-C?, tôi đã phát hiện ra một cách rõ ràng để thoát khỏi tất cả các tập lệnh theo lô từ trong một tập lệnh hoặc tập lệnh CALLed và tất cả thay đổi sau khi SETLOCAL được loại bỏ đúng cách.

@echo off 
setlocal 
set test=AFTER main SETLOCAL 
call :sub 
echo returning from main NEVER REACHED 
exit /b 

:sub 
setlocal 
set test=AFTER sub SETLOCAL 
set test 
call :ExitBatch 
echo returning from sub2 NEVER REACHED 
exit /b 


:ExitBatch - Cleanly exit batch processing, regardless how many CALLs 
if not exist "%temp%\ExitBatchYes.txt" call :buildYes 
call :CtrlC <"%temp%\ExitBatchYes.txt" 1>nul 2>&1 
:CtrlC 
cmd /c exit -1073741510 

:buildYes - Establish a Yes file for the language used by the OS 
pushd "%temp%" 
set "yes=" 
copy nul ExitBatchYes.txt >nul 
for /f "delims=(/ tokens=2" %%Y in (
    '"copy /-y nul ExitBatchYes.txt <nul"' 
) do if not defined yes set "yes=%%Y" 
echo %yes%>ExitBatchYes.txt 
popd 
exit /b 

Đây là đầu ra mẫu chạy test.bat ở trên. Bạn có thể thấy rằng tập lệnh không bao giờ được trả về từ lệnh gọi ExitBatch và định nghĩa biến kiểm tra đã được loại bỏ đúng khi xử lý hàng loạt chấm dứt.

C:\test>test.bat 
test=AFTER sub SETLOCAL 

C:\test>set test 
Environment variable test not defined 

C:\test> 

: Thường trình ExitBatch có thể được đưa vào ExitBatch của riêng nó.bat script và được đặt ở đâu đó trong PATH của bạn sao cho nó có thể được sử dụng thuận tiện bởi bất kỳ tập lệnh batch nào.

@echo off 
:ExitBatch - Cleanly exit batch processing, regardless how many CALLs 
if not exist "%temp%\ExitBatchYes.txt" call :buildYes 
call :CtrlC <"%temp%\ExitBatchYes.txt" 1>nul 2>&1 
:CtrlC 
cmd /c exit -1073741510 

:buildYes - Establish a Yes file for the language used by the OS 
pushd "%temp%" 
set "yes=" 
copy nul ExitBatchYes.txt >nul 
for /f "delims=(/ tokens=2" %%Y in (
    '"copy /-y nul ExitBatchYes.txt <nul"' 
) do if not defined yes set "yes=%%Y" 
echo %yes%>ExitBatchYes.txt 
popd 
exit /b 

Cập nhật Quan trọng:

Nó bây giờ là có thể thực hiện mạnh mẽ xử lý với hàng loạt tinh khiết là ngoại lệ. Bạn không chỉ có thể loại bỏ một ngoại lệ có thể chấm dứt tất cả xử lý hàng loạt từ bất kỳ mức CALL nào, nhưng bạn cũng có thể bắt ngoại lệ ở cấp cao hơn, thực thi mã dọn dẹp xử lý ngoại lệ đặc biệt và tiếp tục xử lý hoặc tiếp tục thoát qua một lần ném khác! Xem Does Windows batch support exception handling? để biết thêm thông tin.

+0

khéo léo. Rất vui khi bạn có thể đặt khám phá của tôi thành công việc tốt một cách nhanh chóng; gần như bù đắp cho sự bối rối nó đã gây ra cho tôi ban đầu. :-) –

+0

Vâng, bạn xứng đáng được chấp nhận, thưa bạn. – Gui13

+0

Giải pháp tốt, cho những trường hợp bình thường sạch hơn tôi. – jeb

20

Đối với một giải pháp tốt xem các phiên bản cải tiến phần

Bạn có thể ngừng một lô bất cứ lúc nào, cũng bên trong gọi hàm lồng nhau.

Bạn chỉ cần tạo lỗi cú pháp, ví dụ với khối trống (), để chặn thông báo lỗi, nó có thể được thực thi trong cuộc gọi và lệnh stderr của cuộc gọi được chuyển hướng đến nul.

@echo off 
setlocal enabledelayedexpansion 

rem Do something 
call :interactive_check 

rem Do something 
call :interactive_check 

goto :eof 

:::::::::::::::::::::::::: 
:interactive_check 
if errorlevel 1 (
    echo. 
    echo /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\ 
    echo Error in compilation process... exiting 
    echo /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\ 
    call :halt 1 
) ELSE (
    echo.Continuing to next step 
) 
goto :eof 

:: Sets the errorlevel and stops the batch immediately 
:halt 
call :__SetErrorLevel %1 
call :__ErrorExit 2> nul 
goto :eof 

:__ErrorExit 
rem Creates a syntax error, stops immediately 
() 
goto :eof 

:__SetErrorLevel 
exit /b %time:~-2% 
goto :eof 

2017-04-09 Phiên bản cải tiến: Exit chỉ lô hiện nay, nhưng không phải là người gọi hàng loạt

Như @dbenham đề cập, có một kỹ thuật mới cho xử lý ngoại lệ mà cũng có thể được sử dụng chỉ để thoát khỏi lô hiện tại.

@echo off 
echo Do something, detecting some fatal error 
call :ExitBatch 3 
exit /b 

:ExitBatch [errorcode] - Exits only the current batch file, regardless how many CALLs 
set _errLevel=%1 
REM *** Remove all calls from the current batch from the call stack 
:popStack 
(
    (goto) 2>nul 
    setlocal DisableDelayedExpansion  
    call set "caller=%%~0" 
    call set _caller=%%caller:~0,1%% 
    call set _caller=%%_caller::=%% 
    if not defined _caller (
     REM callType = func 
     rem set _errLevel=%_errLevel% 
     goto :popStack 
    ) 
    (goto) 2>nul 
    endlocal 
    cmd /c "exit /b %_errLevel%" 
) 
echo NEVER REACHED 
exit /b 
+0

Giải pháp tuyệt vời :) Tôi giải quyết vấn đề bằng cách sử dụng gợi ý của atzz, nhưng trong trường hợp ai đó tình cờ ở đây, bạn sẽ là câu trả lời. – Gui13

+1

Giải pháp này sẽ bảo vệ không đúng các thay đổi môi trường sau SETLOCAL. Tôi đã phát hiện ra một [giải pháp sạch] (http://stackoverflow.com/a/25474648/1012053) mà loại bỏ tất cả các thay đổi sau SETLOCAL đúng cách. – dbenham

1

Tôi đề nghị giải pháp sau đây:

@echo off 

:: comment next line if you want to export any local variables in caller environment 
setlocal 

set FLAG=1 
rem Do something 
call :interactive_check 

set FLAG2=2 
rem Do something 
call :interactive_check 

goto :eof 

:interactive_check 
if errorlevel 1 (
    echo. 
    echo /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\ 
    echo Error in compilation process... exiting 
    echo /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\ 
    (goto) 2>nul & endlocal & exit /b %ERRORLEVEL% 
) else (
    echo Continuing to next step 
) 
goto :eof 

Nó lưu mã lỗi từ lệnh sản xuất lỗi. Nếu bạn muốn lưu mã lỗi để giá trị cụ thể, sau đó sửa đổi dòng:

(goto) 2>nul & endlocal & exit /b YOUR_EXITCODE_HERE 

Tôi không thích giải pháp với lỗi cú pháp (xem jeb's post), bởi vì nó cũng chấm dứt gọi của tập tin thực thi hiện nay cũng có.

1

Khi bạn sử dụng exit /b X để thoát khỏi hàm, nó đặt ERRORLEVEL thành giá trị X. Sau đó, bạn có thể sử dụng ||conditional processing symbol để thực hiện một lệnh khác nếu ERRORLEVEL là khác 0 sau call.

@echo off 
setlocal 
call :myfunction PASS || goto :eof 
call :myfunction FAIL || goto :eof 
echo Execution never gets here 
goto :eof 

:myfunction 
    if "%1"=="FAIL" ( 
     echo myfunction: got a FAIL. Will exit. 
     exit /b 1 
    ) 
    echo myfunction: Everything is good. 
    exit /b 0 

Output từ kịch bản này là:

myfunction: Everything is good. 
myfunction: got a FAIL. Will exit. 
Các vấn đề liên quan