Welcome to Our Website

todo lo que quería saber sobre excepciones

  • 05/23/2020
  • 14 minutos de lectura
    • j
    • f
    • s

el manejo de errores es solo parte de la vida cuando se trata de escribir código. A menudo podemos comprobar y validarcondiciones para el comportamiento esperado. Cuando sucede lo inesperado, recurrimos al manejo de excepciones., Puede manejar fácilmente las excepciones generadas por el código de otras personas o puede generar sus propias excepciones para que otras las manejen.

Nota

La versión original de este artículo apareció en el blog escrito por @KevinMarquette. El equipo de PowerShell agradece a Kevin por compartir este contenido con nosotros. Por favor, echa un vistazo a su blog atPowerShellExplained.com.

terminología Básica

necesitamos cubrir algunos términos básicos antes de entrar en esta.

Exception

Una excepción es como un evento que se crea cuando el manejo normal de errores no puede tratar el problema.,Intentar dividir un número por cero o quedarse sin memoria son ejemplos de algo que crea una excepción. A veces, el autor del código que está utilizando crea excepciones para ciertos problemas cuando ocurren.

lanzar y atrapar

Cuando ocurre una excepción, decimos que se lanza una excepción. Para manejar una excepción lanzada, necesitas atraparla. Si se lanza una excepción y no es atrapada por algo, el script deja de ejecutarse.

la pila de llamadas

La pila de llamadas es la lista de funciones que se han llamado entre sí., Cuando se llama a una función, se agrega a la pila o a la parte superior de la lista. Cuando la función sale o regresa, se elimina de la pila.

cuando se lanza una excepción, esa pila de llamadas se comprueba para que un manejador de excepciones sea catchit.

Errores de terminación y no terminación

una excepción es generalmente un error de terminación. Una excepción lanzada es capturada o termina la ejecución actual. De forma predeterminada, Write-Errorgenera un error que no termina y agrega un error al flujo de salida sin lanzar una excepción.,

señalo esto porque Write-Error y otros errores no terminantes no activan elcatch.

tragar una excepción

esto es cuando se detecta un error solo para suprimirlo. Haga esto con precaución porque puede hacer que los problemas de arranque de problemas sean muy difíciles.

sintaxis básica de comandos

a continuación se muestra un resumen rápido de la sintaxis básica de gestión de excepciones utilizada en PowerShell.

lanzar

para crear nuestro propio evento de excepción, lanzamos una excepción con la palabra clave throw.,

function Start-Something{ throw "Bad thing happened"}

esto crea una excepción de tiempo de ejecución que es un error de terminación. Es manejado por un catch en una función de llamada o sale del script con un mensaje como este.

Write-Error-ErrorAction Stop

mencioné que Write-Error no genera un error de terminación de forma predeterminada. Si se especifica-ErrorAction Stop, Write-Errorgenera un error de terminación que puede ser manejado con uncatch.,

Write-Error -Message "Houston, we have a problem." -ErrorAction Stop

Gracias a Lee Dailey para recordar sobre el uso de -ErrorAction Stop de esta manera.

Cmdlet-ErrorAction Stop

si especifica -ErrorAction Stopen cualquier función o cmdlet avanzado, convierte todas las instrucciones Write-Erroren errores de terminación que detienen la ejecución o que pueden ser manejados por un catch.,

Start-Something -ErrorAction Stop

Try/Catch

la forma en que funciona el manejo de excepciones en PowerShell (y muchos otros lenguajes) es que primero tryuna sección de código y si arroja un error, puede catch. Aquí hay una muestra rápida.

el script catch solo se ejecuta si hay un error de terminación. Si tryse ejecuta correctamente, entonces omite catch.,

Try / Finally

a veces no es necesario manejar un error, pero todavía necesita algún código para ejecutar si una exceptionhappens o no. Un script finally hace exactamente eso.

eche un vistazo a este ejemplo:

cada vez que abra o se conecte a un recurso, debe cerrarlo. Si la excepciónExecuteNonQuery() throwsan, la conexión no se cierra. Aquí está el mismo código dentro de un bloque try/finally.

en este ejemplo, la conexión se cierra si hay un error. También se cierra si no hay error., El script finally se ejecuta cada vez.

debido a que no está capturando la excepción, todavía se propaga a la pila de llamadas.

Try/Catch/Finally

Es perfectamente válido usar catch y finally juntos. La mayoría de las veces usarás uno u otro, pero es posible que encuentres escenarios en los que uses ambos.

Now PSItem

ahora que hemos sacado lo básico del camino, podemos profundizar un poco más.,

dentro del bloque catch, hay una variable automática ($PSItemo $_) de tipo ErrorRecord que contiene los detalles sobre la excepción. Aquí hay un resumen rápido de algunas de las propiedades de las teclas.

para estos ejemplos, utilicé una ruta no válida en ReadAllText para generar esta excepción.

::ReadAllText( '\\test\no\filefound.log')

PSItem.ToString ()

esto le da el mensaje más limpio para usar en el registro y la salida general., ToString()isautomatically called if $PSItem is placed inside a string.

catch{ Write-Output "Ran into an issue: $($PSItem.ToString())"}catch{ Write-Output "Ran into an issue: $PSItem"}

$PSItem.InvocationInfo

Esta propiedad contiene información adicional recopilada por PowerShell sobre la función o scriptdonde se lanzó la excepción. Aquí está el InvocationInfo de la excepción de ejemplo que Icreated.

Los detalles importantes aquí muestran el ScriptName, el Line de código y la etiqueta ScriptLineNumberdonde la invocación comenzó.

PS PSItem.,ScriptStackTrace

esta propiedad muestra el orden de las llamadas a funciones que le llevaron al código donde se generó la excepción.

solo estoy haciendo llamadas a funciones en el mismo script, pero esto rastrearía las llamadas si estuvieran involucrados multiplescripts.

PS PSItem.Exception

Esta es la excepción real que se lanzó.

PS PSItem.Salvedad.Message

Este es el mensaje general que describe la excepción y es un buen punto de partida para el arranque de problemas. La mayoría de las excepciones tienen un mensaje predeterminado, pero también se pueden establecer en algo personalizado cuando se lanza la excepción.,

PS> $PSItem.Exception.MessageException calling "ReadAllText" with "1" argument(s): "The network path was not found."

Este es también el mensaje que se devuelve cuando se llama $PSItem.ToString() si no hay uno en elErrorRecord.

PS PSItem.Salvedad.Las excepciones de InnerException

pueden contener excepciones internas. Este es a menudo el caso cuando el código que está llamando captura una excepción y lanza una excepción diferente. La excepción original se coloca dentro de la nueva excepción.

PS> $PSItem.Exception.InnerExceptionMessageThe network path was not found.

revisaré esto más adelante cuando hable de volver a lanzar excepciones.

PS PSItem.Salvedad.,StackTrace

Este es el StackTrace para la excepción. Mostré un ScriptStackTrace arriba, pero este es para las llamadas a código administrado.

at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)at System.IO.StreamReader..ctor(String path, Encoding encoding, Boolean detectEncodingFromByteOrderMarks, Int32 bufferSize, Boolean checkHost)at System.IO.File.InternalReadAllText(String path, Encoding encoding, Boolean checkHost)at CallSite.Target(Closure , CallSite , Type , String )

solo obtiene este seguimiento de pila cuando el evento se lanza desde el código administrado. Voy a llamar a A.NETframework funciona directamente por lo que es todo lo que podemos ver en este ejemplo. Por lo general, cuando está buscando en un seguimiento de pila, está buscando dónde se detiene su código y comienzan las llamadas al sistema.

trabajando con excepciones

hay más excepciones que la sintaxis básica y las propiedades de excepción.,

captura de excepciones escritas

Puede ser selectivo con las excepciones que capture. Las excepciones tienen un tipo y puede especificar el tipo de excepción que desea capturar.

el tipo de excepción se comprueba para cada bloque catch hasta que se encuentre uno que coincida con su exception.It es importante darse cuenta de que las excepciones pueden heredar de otras excepciones. En el ejemplo anterior,FileNotFoundException hereda de IOException. Así que si el IOException fue primero, entonces se llamaría en su lugar., Solo se invoca un bloque catch incluso si hay varias coincidencias.

Si tuviéramos un System.IO.PathTooLongException, el IOException podría partido, pero si hemos tenido unInsufficientMemoryException entonces nada se captura y se propagan hacia arriba en la pila.

captura varios tipos a la vez

es posible capturar varios tipos de excepción con la misma instrucción catch.

Gracias/u/Sheppard_Ra por sugerir esta adición.

lanzar excepciones con tipo

puede lanzar excepciones con tipo en PowerShell., En lugar de llamar a throw con una cadena:

throw "Could not find: $path"

Use un acelerador de excepciones como este:

throw "Could not find: $path"

pero debe especificar un mensaje cuando lo haga de esa manera.

también puede crear una nueva instancia de una excepción. El mensaje es opcional cuando lo hace porque el sistema tiene mensajes predeterminados para todas las excepciones integradas.

throw ::new()throw ::new("Could not find path: $path")

Si no utiliza PowerShell 5.0 o superior, debe utilizar el enfoque anterior New-Object.,

al usar una excepción escrita, usted (u otros) puede capturar la excepción por el tipo mencionado en la sección anterior.

Write-Error -Excepción

podemos agregar estas escribió excepciones a Write-Error y todavía podemos catch los errores por exceptiontype. Use Write-Error como en estos ejemplos:

entonces podemos atraparlo así:

catch { Write-Log $PSItem.ToString()}

la gran lista de excepciones .NET

compilé una lista maestra con la ayuda de la comunidad Reddit/r/PowerShell que contiene cientos de.,Excepciones netas para complementar este puesto.

  • La gran lista de excepciones.NET

comienzo buscando en esa lista excepciones que sientan que serían una buena opción para la mysituación. Debe intentar usar excepciones en el espacio de nombres base System.

las excepciones son objetos

si comienza a usar muchas excepciones escritas, recuerde que son objetos. Diferentes excepciones tienen diferentes constructores y propiedades., Si nos fijamos en FileNotFoundExceptiondocumentation for System.IO.FileNotFoundException, vemos que podemos pasar un mensaje y una ruta de archivo.

::new("Could not find file", $path)

y tiene una propiedadFileName que expone esa ruta de archivo.

catch { Write-Output $PSItem.Exception.FileName}

debe consultar la documentación de.NET para otros constructores y propiedades de objetos.

Re-lanzar una excepción

Si todo lo que vas a hacer en tu catch bloque throw de la misma excepción, entonces no catches., Solo debe catch una excepción que planea manejar o realizar alguna acción cuando ithappens.

Hay momentos en los que desea realizar una acción en una excepción, pero volver a lanzar la excepción sosomething downstream puede lidiar con ella. Podríamos escribir un mensaje o registrar el problema cerca de donde lo descubramos, pero manejar el problema más arriba en la pila.

catch{ Write-Log $PSItem.ToString() throw $PSItem}

Curiosamente, podemos llamar a throw dentro de la etiqueta catch, y se re-lanza la currentexception.,

catch{ Write-Log $PSItem.ToString() throw}

queremos volver a lanzar la excepción para preservar la información de ejecución original como el código fuente y el número de línea. Si lanzamos una nueva excepción en este punto, oculta dónde comenzó la excepción.

volver a lanzar una nueva excepción

si detecta una excepción pero desea lanzar una diferente, debe anidar la originalexception dentro de la nueva. Esto permite que alguien de la pila acceda a él como$PSItem.Exception.InnerException.

catch{ throw ::new('Could not access field',$PSItem.Exception)}

p PSCmdlet.,ThrowTerminatingError ()

la única cosa que no me gusta de usar throw para excepciones raw es que el mensaje de error apunta a la instrucción throw e indica que la línea es donde está el problema.

tener el mensaje de error me dice que mi script está roto porque llamé a throw en la línea 31 es un mensaje abad para que los usuarios de su script lo vean. No les dice nada útil.

Dexter Dhami señaló que puedo usar ThrowTerminatingError() para corregir eso.,

si asumimos que ThrowTerminatingError() fue llamado dentro de una función llamada Get-Resource, entonces este es el error que veríamos.

¿ves cómo apunta a la función Get-Resource como la fuente del problema? Eso le dice al usuario algo útil.

Porque $PSItem es un ErrorRecord, también podemos usar ThrowTerminatingError esta forma de re-tiro.,

catch{ $PSCmdlet.ThrowTerminatingError($PSItem)}

esto cambia el origen del error al Cmdlet y oculta el funcionamiento interno de la función de los usuarios del Cmdlet.

Try puede crear errores de terminación

Kirk Munro señala que algunas excepciones solo son errores de terminación cuando se ejecutan dentro de un bloque try/catch. Aquí está el ejemplo que me dio que genera una división por cero excepción de tiempo de ejecución.

function Start-Something { 1/(1-1) }

luego invócalo de esta manera para ver que genera el error y aún genera el mensaje.,

&{ Start-Something; Write-Output "We did it. Send Email" }

Pero al colocar el mismo código, dentro de un try/catch, vemos que algo suceda.

try{ &{ Start-Something; Write-Output "We did it. Send Email" }}catch{ Write-Output "Notify Admin to fix error and send email"}

vemos que el error se convierte en un error de terminación y no genera el primer mensaje. Lo que no me gusta de esto es que puede tener este código en una función y actúa de manera diferente si alguien está usando un try/catch.

no me he encontrado con problemas con esto, pero es un caso de esquina para tener en cuenta.

p PSCmdlet.,ThrowTerminatingError () dentro de try/catch

un matiz de $PSCmdlet.ThrowTerminatingError() es que crea un error de terminación dentro de yourCmdlet pero se convierte en un error de no terminación después de salir del Cmdlet. Esto deja al burdenon el llamante de su función para decidir cómo manejar el error. Pueden volver a convertirlo en un error de aterminación usando -ErrorAction Stop o llamándolo desde un try{...}catch{...}.,

plantillas de Función Pública

Una Última toma de una manera que tuve con mi conversación con Kirk Munro fue que coloca untry{...}catch{...} alrededor de cada begin, process y end bloquear todas sus funciones avanzadas. En esos bloques catch genéricos, tiene una sola línea usando$PSCmdlet.ThrowTerminatingError($PSItem) para tratar con todas las excepciones que salen de sus funciones.

function Start-Something{ param() process { try { ... } catch { $PSCmdlet.ThrowTerminatingError($PSItem) } }}

debido a que todo está en una instrucción try dentro de sus funciones, todo actúa consistentemente., Esto también proporciona errores limpios al usuario final que oculta el código interno del error generado.

Trap

me centré en el aspecto try/catch de las excepciones. Pero hay una característica heredada que necesito mencionar antes de terminar esto.

a trap se coloca en un script o función para capturar todas las excepciones que ocurren en ese ámbito. Cuando ocurre una excepción, el código en el trap se ejecuta y luego el código normal continúa. Si ocurren múltiples excepciones, entonces la trampa se llama una y otra vez.,

personalmente nunca adopté este enfoque, pero puedo ver el valor en los scripts de administrador o controlador que registran todas y cada una de las excepciones, y luego continúan ejecutándose.

observaciones de cierre

agregar un manejo adecuado de excepciones a sus scripts no solo los hace más estables, sino que también hace que sea más fácil solucionar esas excepciones.

pasé mucho tiempo hablandothrow porque es un concepto central cuando se habla de exceptionhandling., PowerShell también nos dio Write-Errorque maneja todas las situaciones en las que usaríathrow. Así que no pienses que necesitas usar throw después de leer esto.

ahora que me he tomado el tiempo para escribir sobre el manejo de excepciones en este detalle, voy a cambiar a usar Write-Error -Stop para generar errores en mi código. También voy a seguir el Consejo de takeKirk y hacer ThrowTerminatingError mi controlador de excepciones goto para cada función.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *