batch-file exception exception-handling cmd

batch file - ¿Windows batch admite el manejo de excepciones?



batch-file exception (3)

Bueno, si el término "manejo de excepciones" se toma en el mismo sentido que otros lenguajes de programación, creo que la respuesta es: "NO".

En todos los lenguajes de programación estándar, el término "manejo de excepciones" se refiere a "la ocurrencia, durante el cómputo, de las excepciones: condiciones anómalas o excepcionales que requieren un procesamiento especial", como los errores de tiempo de ejecución que se pueden administrar de una manera diferente a la gestión de errores estándar Realizado por el sistema.

Por ejemplo, en C ++ : "Las excepciones son anomalías en el tiempo de ejecución, como la división por cero, que requieren un manejo inmediato cuando se encuentran con su programa".

El .NET Framework especifica: "Las excepciones representan errores que ocurren durante la ejecución de la aplicación".

En Visual Basic 6 : "Visual Basic admite el manejo de excepciones (errores), que permite al programa detectar y posiblemente recuperarse de errores durante la ejecución".

La descripción de JScript indica: "La instrucción try ... catch ... finally proporciona una manera de manejar algunos o todos los posibles errores que pueden ocurrir en un bloque de código dado, mientras se ejecuta el código".

En todos estos idiomas, "manejo de excepciones" significa administrar un error en tiempo de ejecución que, de lo contrario, causaría la interrupción del programa con un mensaje de error. La forma de hacerlo es a través de la declaración "intentar ... atrapar" de esta manera:

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

Ahora las diferencias frente a la propuesta de código de lote de emulación.

En un archivo por lotes no hay manera de "administrar" un error en tiempo de ejecución: todos los errores en tiempo de ejecución causan que el archivo por lotes detenga la ejecución con un mensaje de error. En los archivos por lotes, de forma diferente a otros idiomas, hay varias situaciones que no se informan como "errores", sino como el resultado manejable de un comando. Por ejemplo, si el comando de búsqueda no puede encontrar la cadena de búsqueda, devuelve un nivel de error mayor que cero, y de una manera completamente equivalente , si el comando set /A produce un "error en tiempo de ejecución" (como división por cero), devuelve un nivel de error mayor que cero y la ejecución continúa normalmente . De esta manera, cualquier programa puede administrar cualquier posible situación de error que se reporte de esta manera a través del código de lote estándar, sin necesidad de "manejo de excepciones".

En la función estándar "intentar ... atrapar", cualquier código que pueda producir cualquier error en el tiempo de ejecución puede colocarse en la parte "intentar" sin más pruebas; la excepción es lanzada automáticamente por el sistema. El error particular que causó la excepción se puede identificar a través de pruebas individuales en la parte de "captura". La emulación de lotes propuesta es completamente diferente. En este caso, cada situación particular de "error" debe ser inspeccionada individualmente en la parte "intentar" para lanzar explícitamente la "excepción" correspondiente; la parte de "captura" también debe procesar cada una de las excepciones dadas.

Este mecanismo se parece más a otra característica estándar de lenguajes de programación: el mecanismo de "administración de eventos" de lenguajes como C ++ que también se admite a través de las Funciones de Manejo de Excepciones de Windows . En este esquema, se genera una excepción / evento explícitamente a través de la función RaiseException, que hace que el subproceso de ejecución salte a la función previamente registrada a través de AddExceptionHandler.

Por favor, no me malinterpretes. Creo que este método es una herramienta valiosa que puede facilitar la gestión de "errores" en el código de lote de una manera simple y poderosa. Sin embargo, no estoy de acuerdo con el esquema propuesto de usar esta función a través del constructo "try ... catch" de los lenguajes de programación estándar, que da la falsa impresión de que es posible emular el mecanismo estándar de manejo de excepciones en los archivos de Windows Batch para poder capturar Errores en tiempo de ejecución. En mi humilde opinión, el método se acercaría a los estándares si se basara en el esquema "RegisterExceptionHandler" y "RaiseException" en su lugar ...

¿La programación por lotes de Windows admite el manejo de excepciones? Si no es así, ¿hay alguna manera de emular efectivamente el manejo de excepciones dentro de los archivos por lotes?

Me gustaría poder "lanzar una excepción" en cualquier lugar dentro de un script por lotes, en cualquier nivel CALL, y hacer que la pila CALL aparezca repetidamente hasta que encuentre un "bloque TRY" activo, por lo que un "bloque CATCH" puede manejar la excepción llene y continúe, o realice alguna limpieza y continúe haciendo estallar la pila de LLAMADAS. Si la excepción nunca se maneja, el procesamiento por lotes termina y el control regresa al contexto de la línea de comandos con un mensaje de error.

Ya existen dos formas publicadas para finalizar el procesamiento por lotes a cualquier profundidad de LLAMADA , pero ninguna de esas técnicas permite ninguna actividad de limpieza estructurada que normalmente se proporcionaría en otros idiomas a través del manejo de excepciones.

Nota: Este es un caso en el que ya conozco una buena respuesta que solo se ha descubierto recientemente y quiero compartir la información.


Las secuencias de comandos por lotes de Windows ciertamente no tienen un manejo formal de excepciones, lo cual no es sorprendente considerando lo primitivo que es el lenguaje. Nunca en mis sueños más salvajes pensé que un manejo efectivo de excepciones podría ser hackeado.

Pero luego se hicieron algunos descubrimientos sorprendentes en un sitio ruso sobre el comportamiento de una declaración GOTO errónea (no tengo idea de lo que se dice, no puedo leer ruso). Se publicó un resumen en inglés en DosTips y se investigó el comportamiento.

Resulta que (GOTO) 2>NUL comporta de forma casi idéntica a EXIT / B, ¡ excepto que los comandos concatenados dentro de un bloque de código ya analizado todavía se ejecutan después del retorno efectivo, dentro del contexto del LLAMADOR!

Aquí hay un breve ejemplo que demuestra la mayoría de los puntos destacados.

@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

- SALIDA -

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

Esta característica es totalmente inesperada, e increíblemente poderosa y útil. Se ha utilizado para:

  • Cree PrintHere.bat : una emulación de la función de documento ''nix here''
  • Cree una utilidad RETURN.BAT que cualquier "función" de lote pueda LLAMAR convenientemente para devolver cualquier valor a través de la barrera de ENDLOCAL, prácticamente sin limitaciones. El código es una versión completa de la idea original de jeb .

Ahora también puedo agregar manejo de excepciones a la lista :-)

La técnica se basa en una utilidad por lotes llamada EXCEPTION.BAT para definir las "macros" de la variable de entorno que se utilizan para especificar los bloques TRY / CATCH, así como para lanzar excepciones.

Antes de poder implementar un bloque TRY / CATCH, las macros deben definirse usando:

call exception init

Luego, los bloques TRY / CATCH se definen con la siguiente sintaxis:

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

Se pueden lanzar excepciones en cualquier momento a través de:

call exception throw errorNumber "messageString" "locationString"

Cuando se lanza una excepción, hace estallar la pila de LLAMADAS utilizando iterativamente (GOTO) 2>NUL hasta que encuentra un TRY / CATCH activo, con lo cual se ramifica al bloque CATCH y ejecuta ese código. Una serie de variables de atributos de excepción están disponibles para el bloque CATCH:

  • exception.Code - El código de excepción numérico
  • exception.Msg - La cadena del mensaje de excepción
  • exception.Loc: la cadena que describe la ubicación donde se lanzó la excepción
  • exception.Stack: una cadena que rastrea la pila de llamadas desde el bloque CATCH (o la línea de comando si no se detecta), hasta el origen de la excepción.

Si la excepción se maneja por completo, entonces la excepción debe borrarse mediante el borrado de la call exception clear , y la secuencia de comandos continúa normalmente. Si la excepción no se maneja por completo, se puede lanzar una nueva excepción con una excepción completamente nueva. Apilar, o la pila antigua se puede conservar con

call exception rethrow errorNumber "messageString" "locationString"

Si no se maneja una excepción, se imprime un mensaje de "excepción no manejada", incluidos los cuatro atributos de excepción, se termina todo el procesamiento por lotes y el control se devuelve al contexto de la línea de comandos.

Aquí está el código que hace que todo esto sea posible: la documentación completa está integrada en el script y está disponible desde la línea de comandos a través de la exception help exception /? o la exception /? .

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

A continuación se encuentra el script para probar las capacidades de EXCEPTION.BAT. El script recursivamente se llama a sí mismo 7 veces. Cada iteración tiene dos LLAMADAS, una a una etiqueta: que muestra la propagación de excepción normal, y la otra a una secuencia de comandos que muestra la propagación de excepciones a través de las LLAMADAS de secuencia de comandos.

Al regresar de una llamada recursiva, lanza una excepción si el recuento de iteraciones es un múltiplo de 3 (iteraciones 3 y 6).

Cada CALL tiene su propio controlador de excepciones que normalmente informa la excepción y luego vuelve a emitir una excepción modificada. Pero si el recuento de iteraciones es 5, entonces se maneja la excepción y se reanuda el procesamiento normal.

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

- SALIDA -

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!]

Finalmente, aquí hay una serie de scripts triviales que muestran cómo se pueden usar las excepciones de manera efectiva, incluso cuando los scripts intermedios no saben nada sobre ellas.

Comience con una sencilla utilidad de script de división que divide dos números e imprime el resultado:

dividir

:: 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

Observe cómo el script lanza una excepción si detecta un error, pero no hace nada para detectar la excepción.

Ahora escribiré un arnés de prueba de división que es totalmente ingenuo acerca de las excepciones de lotes.

testDivide.bat

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

--SALIDA--

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]

Observe cómo el ECHO final nunca se ejecuta porque la excepción provocada por divide.bat no se manejó.

Finalmente, escribiré un script maestro que llama a la ingenua prueba de división y maneja adecuadamente la excepción:

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!

- SALIDA -

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

El script maestro fue capaz de capturar con éxito una excepción generada por divide.bat, aunque tuvo que pasar por testDivide.bat, que no sabe nada acerca de las excepciones. Muy genial :-)

Ahora bien, esto ciertamente no es una panacea para todo lo relacionado con el manejo de errores:

  • Hay una serie de limitaciones sintácticas y de diseño de código que se describen completamente en la documentación incorporada. Pero nada demasiado atroz.

  • No hay forma de tratar automáticamente todos los errores como excepciones. Todas las excepciones deben ser lanzadas explícitamente por código. Probablemente esto sea algo bueno, dado que la notificación de errores se maneja por convención, no hay reglas estrictas. Algunos programas no siguen la convención. Por ejemplo, HELP ValidCommand devuelve ERRORLEVEL 1, que por convención implica un error, mientras que HELP InvalidCommand devuelve ERRORLEVEL 0, lo que implica éxito.

  • Esta técnica de excepción por lotes no puede detectar y manejar errores fatales en el tiempo de ejecución. Por ejemplo, GOTO :NonExistentLabel aún terminará inmediatamente todo el procesamiento por lotes, sin ninguna oportunidad de detectar el error.

Puede seguir el desarrollo de EXCEPTION.BAT en http://www.dostips.com/forum/viewtopic.php?f=3&t=6497 . Cualquier desarrollo futuro será publicado allí. Probablemente no actualizaré esta publicación de .


utilizando el successCmd && ( failingCmd & (call ) ) || ( excHandlingCmd ) successCmd && ( failingCmd & (call ) ) || ( excHandlingCmd ) sintaxis de successCmd && ( failingCmd & (call ) ) || ( excHandlingCmd ) es bastante buena para muchos casos y no requiere ningún archivo adicional como también se describe aquí: https://.com/a/17085933/1915920

(el dummy (call ) es solo para el caso de que el segundo (último) cmd falle)