2010-07-12 58 views
14

我的批處理文件有問題。 它通過做這樣的事情會自動建立幾個方案:從函數內退出批處理腳本

  • 設置一些編譯標誌
  • 運行「GMAKE所有」
  • 呼叫「檢查錯誤級別」功能,如果錯誤級別1,出口

所以它看起來像這樣:

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

有6或這7(和我t可能會增長)。所以我做了一個函數來檢查錯誤級別,而不是每一步都複製/粘貼它。 問題是這樣的:在錯誤檢驗通過函數取得:

: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 

現在,運行它時,該exit /B 1簡單地退出功能,而不是批處理文件

你知道如何退出整個批處理文件,而不必在每一步都複製/粘貼我的「if errorlevel 1 ..」嗎?

+2

你不能。只需將'call:interactive_check'替換爲'errorlevel 1 goto error'。複製粘貼前者或後者沒有太大區別。 :) – atzz 2010-07-12 10:57:25

+0

明白了,解決了這個問題,它工作正常,謝謝! – Gui13 2010-07-12 12:03:25

回答

16

使用致命的語法錯誤,如傑布證明,確實殺死所有的批量處理,但它也有一個討厭的副作用 - SETLOCAL後環境的變化將被保留,即使他們應該通過隱含的ENDLOCAL被丟棄批處理結束時。請參閱我的DosTips文章SETLOCAL continues after batch termination!瞭解更多信息。

根據Why does a non-interactive batch script think I've pressed control-C?的信息,我發現了一種乾淨的方式來退出CALLed例程或腳本中的所有批處理腳本,並且在SETLOCAL之後的所有更改都被正確丟棄。

@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 

以下是運行上述test.bat的示例輸出。您可以看到腳本從不從:ExitBatch調用返回,並且一旦批處理終止,測試變量定義就已被正確丟棄。

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

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

C:\test> 

:ExitBatch例程可以放入它自己的ExitBatch中。蝙蝠腳本並放置在PATH中的某個位置,以便任何批處理腳本都可以方便地使用它。

@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 

重要更新:

現在可以實現強大的例外純批量處理。您不僅可以拋出一個可以終止來自任何CALL級別的所有批處理的異常,還可以在更高級別捕獲異常,優先執行特殊的異常處理清除代碼並恢復處理,或者通過另一次拋出繼續退出!有關更多信息,請參見Does Windows batch support exception handling?

+0

巧妙。很高興你能把我的發現很快做好,幾乎彌補了原來它給我帶來的困惑。 :-) – 2014-08-24 20:33:02

+0

那麼,你應該接受,先生。 – Gui13 2014-08-26 12:02:19

+0

很好的解決方案,對於比我的清潔工更普通的情況。 – jeb 2014-08-27 14:58:52

20

對於一個很好的解決方案看到部分改進版

您可以隨時停止批,也在裏面嵌套函數調用。

您只需要創建語法錯誤(例如用空塊())來抑制錯誤消息,它可以在調用中執行,並且調用的stderr被重定向到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年4月9日的改進版:僅退出當前批處理,但不是調用批處理

正如@dbenham提到的,異常處理新TECHNIC那也可以用僅退出當前批次。

@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

很酷的解決方案:) 我使用atzz的建議解決了這個問題,但如果有人在這裏絆倒,你會成爲答案。 – Gui13 2011-03-21 12:54:02

+1

此解決方案將在SETLOCAL之後不正確地保留環境更改。我發現了一個[clean solution](http://stackoverflow.com/a/25474648/1012053),它正確放棄了SETLOCAL之後的所有更改。 – dbenham 2014-08-24 18:00:26

1

我建議如下解決方案:

@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 

它從其中產生誤差的指令保存錯誤代碼。如果你想保存錯誤代碼特定的值,然後修改行:

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

我不喜歡和語法錯誤的解決方案(見jeb's post),因爲它也終止當前的批處理文件的調用者也。

1

當您使用exit /b X退出該功能時,它會將ERRORLEVEL設置爲X的值。如果ERRORLEVELcall之後非零,則可以使用||conditional processing symbol執行另一個命令。從這個腳本

@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 

輸出是:

myfunction: Everything is good. 
myfunction: got a FAIL. Will exit.