Windows批处理是否支持异常处理?

时间:2015-07-16 04:24:01

标签: batch-file exception exception-handling cmd

Windows批处理编程是否支持异常处理?如果没有,有没有办法有效地模拟批处理文件中的异常处理?

我希望能够"抛出异常"批处理脚本中的任何位置,在任何CALL级别,并且重复弹出CALL堆栈,直到它找到一个活动的" TRY块",然后一个" CATCH块"可以完全处理异常并继续执行,或者进行一些清理并继续弹出CALL堆栈。如果永远不处理异常,则终止批处理并且控制返回到命令行上下文并显示错误消息。

已经有couple posted ways to terminate batch processing at any CALL depth,但这些技术都不允许通过异常处理通常在其他语言中提供的任何结构化清理活动。

注意: 这是一个我已经知道最近才被发现的好答案的情况,我想分享信息

3 个答案:

答案 0 :(得分:30)

Windows批处理脚本当然没有任何正式的异常处理 - 考虑到语言的原始性,这一点并不令人惊讶。从来没有在我最疯狂的梦想中,我曾经认为有效的异常处理可能会被黑客攻击。

但是后来Russian site对一个错误的GOTO陈述的行为做了一些惊人的发现(我不知道所说的是什么,我不能读俄语)。 An English summary was posted at DosTips,行为进一步调查。

事实证明,(GOTO) 2>NUL与EXIT / B,几乎完全相同,但已经解析的代码块中的连接命令仍然在有效返回后执行, 在CALLer的背景下!

这是一个简短的例子,展示了大部分要点。

@echo off
setlocal enableDelayedExpansion
set "var=Parent Value"
(
  call :test
  echo This and the following line are not executed
  exit /b
)
:break
echo How did I get here^^!^^!^^!^^!
exit /b

:test
setlocal disableDelayedExpansion
set "var=Child Value"
(goto) 2>nul & echo var=!var! & goto :break
echo This line is not executed

:break
echo This line is not executed

- 输出 -

var=Parent Value
How did I get here!!!!

此功能完全出乎意料,非常强大且实用。它已被用于:

现在我还可以在列表中添加异常处理: - )

该技术依赖于名为EXCEPTION.BAT的批处理实用程序来定义环境变量"宏"用于指定TRY / CATCH块以及抛出异常。

在实现TRY / CATCH块之前,必须使用以下命令定义宏:

call exception init

然后使用以下语法定义TRY / CATCH块:

:calledRoutine
setlocal
%@Try%
  REM normal code goes here
%@EndTry%
:@Catch
  REM Exception handling code goes here
:@EndCatch

可以通过以下方式随时抛出异常:

call exception throw  errorNumber  "messageString"  "locationString"

当抛出异常时,它会使用(GOTO) 2>NUL以迭代方式弹出CALL堆栈,直到它找到一个活动的TRY / CATCH,然后它分支到CATCH块并执行该代码。 CATCH块可以使用一系列异常属性变量:

  • exception.Code - 数字异常代码
  • exception.Msg - 异常消息字符串
  • exception.Loc - 描述抛出异常的位置的字符串
  • exception.Stack - 跟踪来自CATCH块(或命令行,如果未捕获)的调用堆栈的字符串,一直到异常原点。

如果完全处理异常,则应通过call exception clear清除异常,并且脚本正常运行。如果没有完全处理异常,那么可以抛出一个新的异常,带有一个全新的异常.Stack,或旧的堆栈可以用

保存
call exception rethrow  errorNumber  "messageString"  "locationString"

如果没有处理异常,那么"未处理的异常"打印消息,包括四个异常属性,终止所有批处理,并将控制返回到命令行上下文。

以下代码可实现所有这些功能 - 完整文档嵌入在脚本中,可通过exception helpexception /?从命令行获取。

<强> EXCEPTION.BAT

::EXCEPTION.BAT Version 1.4
::
:: Provides exception handling for Windows batch scripts.
::
:: Designed and written by Dave Benham, with important contributions from
:: DosTips users jeb and siberia-man
::
:: Full documentation is at the bottom of this script
::
:: History:
::   v1.4 2016-08-16  Improved detection of command line delayed expansion
::                    using an original idea by jeb
::   v1.3 2015-12-12  Added paged help option via MORE
::   v1.2 2015-07-16  Use COMSPEC instead of OS to detect delayed expansion
::   v1.1 2015-07-03  Preserve ! in exception attributes when delayed expansion enabled
::   v1.0 2015-06-26  Initial versioned release with embedded documentation
::
@echo off
if "%~1" equ "/??" goto pagedHelp
if "%~1" equ "/?" goto help
if "%~1" equ "" goto help
shift /1 & goto %1


:throw  errCode  errMsg  errLoc
set "exception.Stack="
:: Fall through to :rethrow


:rethrow  errCode  errMsg  errLoc
setlocal disableDelayedExpansion
if not defined exception.Restart set "exception.Stack=[%~1:%~2] %exception.Stack%"
for /f "delims=" %%1 in ("%~1") do for /f "delims=" %%2 in ("%~2") do for /f "delims=" %%3 in ("%~3") do (
  setlocal enableDelayedExpansion
  for /l %%# in (1 1 10) do for /f "delims=" %%S in (" !exception.Stack!") do (
    (goto) 2>NUL
    setlocal enableDelayedExpansion
    if "!!" equ "" (
      endlocal
      setlocal disableDelayedExpansion
      call set "funcName=%%~0"
      call set "batName=%%~f0"
      if defined exception.Restart (set "exception.Restart=") else call set "exception.Stack=%%funcName%%%%S"
      setlocal EnableDelayedExpansion
      if !exception.Try! == !batName!:!funcName! (
        endlocal
        endlocal
        set "exception.Code=%%1"
        if "!!" equ "" (
          call "%~f0" setDelayed
        ) else (
          set "exception.Msg=%%2"
          set "exception.Loc=%%3"
          set "exception.Stack=%%S"
        )
        set "exception.Try="
        (CALL )
        goto :@Catch
      )
    ) else (
      for %%V in (Code Msg Loc Stack Try Restart) do set "exception.%%V="
      if "^!^" equ "^!" (
        call "%~f0" showDelayed
      ) else (
        echo(
        echo Unhandled batch exception:
        echo   Code = %%1
        echo   Msg  = %%2
        echo   Loc  = %%3
        echo   Stack=%%S
      )
      echo on
      call "%~f0" Kill
    )>&2
  )
  set exception.Restart=1
  setlocal disableDelayedExpansion
  call "%~f0" rethrow %1 %2 %3
)
:: Never reaches here


:init
set "@Try=call set exception.Try=%%~f0:%%~0"
set "@EndTry=set "exception.Try=" & goto :@endCatch"
:: Fall through to :clear


:clear
for %%V in (Code Msg Loc Stack Restart Try) do set "exception.%%V="
exit /b


:Kill - Cease all processing, ignoring any remaining cached commands
setlocal disableDelayedExpansion
if not exist "%temp%\Kill.Yes" call :buildYes
call :CtrlC <"%temp%\Kill.Yes" 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 Kill.Yes >nul
for /f "delims=(/ tokens=2" %%Y in (
  '"copy /-y nul Kill.Yes <nul"'
) do if not defined yes set "yes=%%Y"
echo %yes%>Kill.Yes
popd
exit /b


:setDelayed
setLocal disableDelayedExpansion
for %%. in (.) do (
  set "v2=%%2"
  set "v3=%%3"
  set "vS=%%S"
)
(
  endlocal
  set "exception.Msg=%v2:!=^!%"
  set "exception.Loc=%v3:!=^!%"
  set "exception.Stack=%vS:!=^!%"
)
exit /b


:showDelayed -
setLocal disableDelayedExpansion
for %%. in (.) do (
  set "v2=%%2"
  set "v3=%%3"
  set "vS=%%S"
)
for /f "delims=" %%2 in ("%v2:!=^!%") do for /f "delims=" %%3 in ("%v3:!=^!%") do for /f "delims=" %%S in ("%vS:!=^!%") do (
  endlocal
  echo(
  echo Unhandled batch exception:
  echo   Code = %%1
  echo   Msg  = %%2
  echo   Loc  = %%3
  echo   Stack=%%S
)
exit /b


:-?
:help
setlocal disableDelayedExpansion
for /f "delims=:" %%N in ('findstr /rbn ":::DOCUMENTATION:::" "%~f0"') do set "skip=%%N"
for /f "skip=%skip% tokens=1* delims=:" %%A in ('findstr /n "^" "%~f0"') do echo(%%B
exit /b


:-??
:pagedHelp
setlocal disableDelayedExpansion
for /f "delims=:" %%N in ('findstr /rbn ":::DOCUMENTATION:::" "%~f0"') do set "skip=%%N"
((for /f "skip=%skip% tokens=1* delims=:" %%A in ('findstr /n "^" "%~f0"') do @echo(%%B)|more /e) 2>nul
exit /b


:-v
:/v
:version
echo(
for /f "delims=:" %%A in ('findstr "^::EXCEPTION.BAT" "%~f0"') do echo %%A
exit /b


:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:::DOCUMENTATION:::

EXCEPTION.BAT is a pure batch script utility that provides robust exception
handling within batch scripts. It enables code to be placed in TRY/CATCH blocks.
If no exception is thrown, then only code within the TRY block is executed.
If an exception is thrown, the batch CALL stack is popped repeatedly until it
reaches an active TRY block, at which point control is passed to the associated
CATCH block and normal processing resumes from that point. Code within a CATCH
block is ignored unless an exception is thrown.

An exception may be caught in a different script from where it was thrown.

If no active TRY is found after throwing an exception, then an unhandled
exception message is printed to stderr, all processing is terminated within the
current CMD shell, and control is returned to the shell command line.

TRY blocks are specified using macros. Obviously the macros must be defined
before they can be used. The TRY macros are defined using the following CALL

    call exception init

Besides defining @Try and @EndTry, the init routine also explicitly clears any
residual exception that may have been left by prior processing.

A TRY/CATCH block is structured as follows:

    %@Try%
      REM any normal code goes here
    %@EndTry%
    :@Catch
      REM exception handling code goes here
    :@EndCatch

- Every TRY must have an associated CATCH.

- TRY/CATCH blocks cannot be nested.

- Any script or :labeled routine that uses TRY/CATCH must have at least one
  SETLOCAL prior to the appearance of the first TRY.

- TRY/CATCH blocks use labels, so they should not be placed within parentheses.
  It can be done, but the parentheses block is broken when control is passed to
  the :@Catch or :@EndCatch label, and the code becomes difficult to interpret
  and maintain.

- Any valid code can be used within a TRY or CATCH block, including CALL, GOTO,
  :labels, and balanced parentheses. However, GOTO cannot be used to leave a
  TRY block. GOTO can only be used within a TRY block if the label appears
  within the same TRY block.

- GOTO must never transfer control from outside TRY/CATCH to within a TRY or
  CATCH block.

- CALL should not be used to call a label within a TRY or CATCH block.

- CALLed routines containing TRY/CATCH must have labels that are unique within
  the script. This is generally good batch programming practice anyway.
  It is OK for different scripts to share :label names.

- If a script or routine recursively CALLs itself and contains TRY/CATCH, then
  it must not throw an exception until after execution of the first %@Try%

Exceptions are thrown by using

    call exception throw  Code  Message  Location

where

    Code = The numeric code value for the exception.

    Message = A description of the exception.

    Location = A string that helps identify where the exception occurred.
               Any value may be used. A good generic value is "%~f0[%~0]",
               which expands to the full path of the currently executing
               script, followed by the currently executing routine name
               within square brackets.

The Message and Location values must be quoted if they contain spaces or poison
characters like & | < >. The values must not contain additional internal quotes,
and they must not contain a caret ^.

The following variables will be defined for use by the CATCH block:

  exception.Code  = the Code value
  exception.Msg   = the Message value
  exception.Loc   = the Location value
  exception.Stack = traces the call stack from the CATCH block (or command line
                    if not caught), all the way to the exception.

If the exception is not caught, then all four values are printed as part of the
"unhandled exception" message, and the exception variables are not defined.

A CATCH block should always do ONE of the following at the end:

- If the exception has been handled and processing can continue, then clear the
  exception definition by using

    call exception clear

  Clear should never be used within a Try block.

- If the exception has not been fully handled, then a new exception should be
  thrown which can be caught by a higher level CATCH. You can throw a new
  exception using the normal THROW, which will clear exception.Stack and any
  higher CATCH will have no awareness of the original exception.

  Alternatively, you may rethrow an exception and preserve the exeption stack
  all the way to the original exception:

    call exception rethrow  Code  Message  Location

  It is your choice as to whether you want to pass the original Code and/or
  Message and/or Location. Either way, the stack will preserve all exceptions
  if rethrow is used.

  Rethrow should only be used within a CATCH block.


One last restriction - the full path to EXCEPTION.BAT must not include ! or ^.


This documentation can be accessed via the following commands

    constant stream:   exception /?   OR  exception help
    paged via MORE:    exception /??  OR  exception pagedHelp

The version of this utility can be accessed via

    exception /v  OR  exception version


EXCEPTION.BAT was designed and written by Dave Benham, with important
contributions from DosTips users jeb and siberia-man.

Development history can be traced at:
  http://www.dostips.com/forum/viewtopic.php?f=3&t=6497

下面是测试EXCEPTION.BAT功能的脚本。该脚本递归调用自身7次。每次迭代都有两个CALL,一个用于:标签,用于演示正常的异常传播,另一个用于演示跨脚本CALL的异常传播的脚本。

从递归调用返回时,如果迭代计数是3的倍数(迭代3和6),则抛出异常。

每个CALL都有自己的异常处理程序,通常会报告异常,然后重新抛出修改后的异常。但如果迭代计数为5,则处理异常并恢复正常处理。

@echo off

:: Main
setlocal enableDelayedExpansion
if not defined @Try call exception init

set /a cnt+=1
echo Main Iteration %cnt% - Calling :Sub
%@Try%
(
  call :Sub
  call echo Main Iteration %cnt% - :Sub returned %%errorlevel%%
)
%@EndTry%
:@Catch
  setlocal enableDelayedExpansion
  echo(
  echo Main Iteration %cnt% - Exception detected:
  echo   Code     = !exception.code!
  echo   Message  = !exception.msg!
  echo   Location = !exception.loc!
  echo Rethrowing modified exception
  echo(
  endlocal
  call exception rethrow -%cnt% "Main Exception^!" "%~f0<%~0>"
:@EndCatch
echo Main Iteration %cnt% - Exit
exit /b %cnt%


:Sub
setlocal
echo :Sub Iteration %cnt% - Start
%@Try%
  if %cnt% lss 7 (
    echo :Sub Iteration %cnt% - Calling "%~f0"
    call "%~f0"
    %= Show any non-exception return code (demonstrate ERRORLEVEL is preserved if no exception) =%
    call echo :Sub Iteration %cnt% - testException returned %%errorlevel%%
  )
  %= Throw an exception if the iteration count is a multiple of 3 =%
  set /a "1/(cnt%%3)" 2>nul || (
    echo Throwing exception
    call exception throw -%cnt% "Divide by 0 exception^!" "%~f0<%~0>"
  )
%@EndTry%
:@Catch
  setlocal enableDelayedExpansion
  echo(
  echo :Sub Iteration %cnt% - Exception detected:
  echo   Code     = !exception.code!
  echo   Message  = !exception.msg!
  echo   Location = !exception.loc!
  endlocal
  %= Handle the exception if iteration count is a multiple of 5, else rethrow it with new properties =%
  set /a "1/(cnt%%5)" 2>nul && (
    echo Rethrowing modified exception
    echo(
    call exception rethrow -%cnt% ":Sub Exception^!" "%~f0<%~0>"
  ) || (
    call exception clear
    echo Exception handled
    echo(
  )
:@EndCatch
echo :Sub Iteration %cnt% - Exit
exit /b %cnt%

- 输出 -

Main Iteration 1 - Calling :Sub
:Sub Iteration 1 - Start
:Sub Iteration 1 - Calling "C:\test\testException.bat"
Main Iteration 2 - Calling :Sub
:Sub Iteration 2 - Start
:Sub Iteration 2 - Calling "C:\test\testException.bat"
Main Iteration 3 - Calling :Sub
:Sub Iteration 3 - Start
:Sub Iteration 3 - Calling "C:\test\testException.bat"
Main Iteration 4 - Calling :Sub
:Sub Iteration 4 - Start
:Sub Iteration 4 - Calling "C:\test\testException.bat"
Main Iteration 5 - Calling :Sub
:Sub Iteration 5 - Start
:Sub Iteration 5 - Calling "C:\test\testException.bat"
Main Iteration 6 - Calling :Sub
:Sub Iteration 6 - Start
:Sub Iteration 6 - Calling "C:\test\testException.bat"
Main Iteration 7 - Calling :Sub
:Sub Iteration 7 - Start
:Sub Iteration 7 - Exit
Main Iteration 7 - :Sub returned 7
Main Iteration 7 - Exit
:Sub Iteration 6 - testException returned 7
Throwing exception

:Sub Iteration 6 - Exception detected:
  Code     = -6
  Message  = Divide by 0 exception!
  Location = C:\test\testException.bat<:Sub>
Rethrowing modified exception


Main Iteration 6 - Exception detected:
  Code     = -6
  Message  = :Sub Exception!
  Location = C:\test\testException.bat<:Sub>
Rethrowing modified exception


:Sub Iteration 5 - Exception detected:
  Code     = -6
  Message  = Main Exception!
  Location = C:\test\testException.bat<C:\test\testException.bat>
Exception handled

:Sub Iteration 5 - Exit
Main Iteration 5 - :Sub returned 5
Main Iteration 5 - Exit
:Sub Iteration 4 - testException returned 5
:Sub Iteration 4 - Exit
Main Iteration 4 - :Sub returned 4
Main Iteration 4 - Exit
:Sub Iteration 3 - testException returned 4
Throwing exception

:Sub Iteration 3 - Exception detected:
  Code     = -3
  Message  = Divide by 0 exception!
  Location = C:\test\testException.bat<:Sub>
Rethrowing modified exception


Main Iteration 3 - Exception detected:
  Code     = -3
  Message  = :Sub Exception!
  Location = C:\test\testException.bat<:Sub>
Rethrowing modified exception


:Sub Iteration 2 - Exception detected:
  Code     = -3
  Message  = Main Exception!
  Location = C:\test\testException.bat<C:\test\testException.bat>
Rethrowing modified exception


Main Iteration 2 - Exception detected:
  Code     = -2
  Message  = :Sub Exception!
  Location = C:\test\testException.bat<:Sub>
Rethrowing modified exception


:Sub Iteration 1 - Exception detected:
  Code     = -2
  Message  = Main Exception!
  Location = C:\test\testException.bat<C:\test\testException.bat>
Rethrowing modified exception


Main Iteration 1 - Exception detected:
  Code     = -1
  Message  = :Sub Exception!
  Location = C:\test\testException.bat<:Sub>
Rethrowing modified exception


Unhandled batch exception:
  Code = -1
  Msg  = Main Exception!
  Loc  = C:\test\testException.bat<testException>
  Stack= testException [-1:Main Exception!]  :Sub [-1::Sub Exception!]  C:\test\testException.bat [-2:Main Exception!]  :Sub [-2::Sub Exception!]  C:\test\testException.bat [-3:Main Exception!]  :Sub [-3::Sub Exception!]  [-3:Divide by 0 exception!]

最后,这里有一系列简单的脚本,展示了即使中间脚本对它们一无所知,如何有效地使用异常!

从一个简单的除法脚本实用程序开始,该实用程序将两个数字分开并打印结果:

<强> divide.bat

:: divide.bat  numerator  divisor
@echo off
setlocal
set /a result=%1 / %2 2>nul || call exception throw -100 "Division exception" "divide.bat"
echo %1 / %2 = %result%
exit /b

请注意如果脚本检测到错误,脚本会抛出异常,但它不会捕获异常。

现在,我将编写一个对批处理异常完全天真的分裂测试工具。

<强> testDivide.bat

@echo off
for /l %%N in (4 -1 0) do call divide 12 %%N
echo Finished successfully!

- 输出 -

C:\test>testDivide
12 / 4 = 3
12 / 3 = 4
12 / 2 = 6
12 / 1 = 12

Unhandled batch exception:
  Code = -100
  Msg  = Division exception
  Loc  = divide.bat
  Stack= testDivide divide [-100:Division exception]

注意最终的ECHO永远不会执行,因为divide.bat引发的异常没有被处理。

最后,我将编写一个调用naive testDivide的主脚本并正确处理异常:

<强> master.bat

@echo off
setlocal
call exception init

%@Try%
  call testDivide
%@EndTry%
:@Catch
  echo %exception.Msg% detected and handled
  call exception clear
:@EndCatch
echo Finished Successfully!

- 输出 -

C:\test>master
12 / 4 = 3
12 / 3 = 4
12 / 2 = 6
12 / 1 = 12
Division exception detected and handled
Finished Successfully!

主脚本能够成功捕获由divide.bat引发的异常,即使它必须通过testDivide.bat,它对异常一无所知。非常酷: - )

现在,这肯定不是解决与错误处理相关的所有问题的灵丹妙药:

  • 内置文档中详细描述了许多语法和代码布局限制。但没什么太可怕的。

  • 无法将所有错误自动视为例外。必须通过代码显式抛出所有异常。这可能是一件好事,因为错误报告是按惯例处理的 - 没有严格的规则。有些计划不遵守惯例。例如,HELP ValidCommand返回ERRORLEVEL 1,按惯例,它返回错误,而HELP InvalidCommand返回ERRORLEVEL 0,这意味着成功。

  • 此批处理异常技术无法捕获和处理致命的运行时错误。例如,GOTO :NonExistentLabel仍将立即终止所有批处理,没有任何机会捕获错误。

您可以在http://www.dostips.com/forum/viewtopic.php?f=3&t=6497关注EXCEPTION.BAT的开发。任何未来的发展都将在那里发布。我可能不会更新这个StackOverflow帖子。

答案 1 :(得分:2)

好吧,如果术语&#34;异常处理&#34;与其他编程语言一样,我认为答案是:&#34; NO&#34;。

在所有标准编程语言中,术语&#34;异常处理&#34; refers to&#34;计算期间发生的异常 - 需要特殊处理的异常或异常情况&#34;,如运行时错误,可能以不同于标准错误处理方式管理的运行时错误系统。

例如,in C++:&#34;例外是运行时异常,例如除以零,需要在程序遇到时立即处理&#34;。

.NET Framework指定:&#34;异常表示在应用程序执行期间发生的错误&#34;。

Visual Basic 6中:&#34; Visual Basic支持异常(错误)处理,允许程序在执行期间检测并可能从错误中恢复。&#34;

JScript描述表明:&#34; try ... catch ... finally语句提供了一种方法来处理给定代码块中可能发生的部分或全部错误,同时仍在运行代码&#34;。

所有这些语言&#34;异常处理&#34;表示管理运行时错误否则会导致程序中断并显示错误消息。这样做的方法是通过&#34;尝试...捕获&#34;这样说:

try {
   *any* code 
   that may cause a *run-time ERROR*
}
catch (exception) {
   code that allows to *identify the error*
   testing specific values of "exception"
}

现在差异与建议的批处理文件代码模拟有关。

在批处理文件中,无法来管理&#34;运行时错误:所有运行时错误都会导致批处理文件停止执行并显示错误消息。在批处理文件中,以与其他语言不同的方式,有几种情况报告为&#34;错误&#34;,但正如命令的可管理结果一样。例如,如果find命令无法找到搜索字符串,则返回错误级别大于零,而以完全等效的方式,如果set /A命令生成&# 34;运行时错误&#34; (如除以零),它返回大于零的错误级别,并且通常继续执行。这样,任何程序都可以管理通过标准批处理代码以这种方式报告的任何可能的错误情况,而不需要&#34;异常处理&#34;。

在标准&#34;尝试...赶上&#34;功能,可能产生任何运行时错误任何代码都可以放在&#34; try&#34;没有进一步测试的部分;系统会自动抛出 异常。导致异常的特定错误可以通过&#34; catch&#34;中的单个测试来识别。部分。建议的批量仿真完全不同。在这种情况下,每个特定的&#34;错误&#34;情况必须在&#34;尝试&#34;中单独检查部分以便明确地抛出相应的&#34;异常&#34 ;; &#34;赶上&#34; part还必须处理每个给定的异常。

这种机制看起来更像是另一种标准的编程语言功能:&#34;事件管理&#34;语言机制like C++也通过Windows Exception Handling Functions支持。在此方案中,异常/事件通过RaiseException函数显式引发,这导致执行线程跳转到先前通过AddExceptionHandler注册的函数。

请不要误解我。我认为这种方法是一种有价值的工具,可以帮助管理错误&#34;在批处理代码中以简单而有力的方式。但是,我不同意通过&#34; try ... catch&#34;使用此功能的建议方案。标准编程语言的构造,给出了错误的印象,可以模拟Windows批处理文件中的标准异常处理机制,以捕获运行时错误。在我看来,如果它基于&#34; RegisterExceptionHandler&#34;那么该方法将接近标准。和#34; RaiseException&#34;而不是......

答案 2 :(得分:0)

使用successCmd && ( failingCmd & (call ) ) || ( excHandlingCmd )语法非常适合很多情况,并且不需要任何其他文件,如下所述:https://stackoverflow.com/a/17085933/1915920

(虚拟(call )仅适用于第二个(最后一个)cmd失败的情况)