2010-07-12 49 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.