debugging - sirve - ¿Hay una manera de entrar en el depurador en un error?
que es un debug y para que sirve (4)
¿Hay alguna manera de ingresar al depurador de PowerShell en respuesta a un error? El parámetro ErrorAction
tiene varios valores, pero no veo nada como Debug
. Lo que me gustaría es abrir el depurador como si se hubiera abierto un punto de interrupción, pero solo cuando se produce un error (de, por ejemplo, Write-Error
).
Editar Debo aclarar un poco: soy principalmente un desarrollador de C # y algo nuevo en PowerShell, lo que estoy esperando es un comportamiento similar al que el depurador de Visual Studio le brinda para las "excepciones no controladas". Parece que es más común que los comandos de PowerShell inicien excepciones, mientras que las secuencias de comandos personalizadas parecen usar principalmente Error de escritura. No creo que me importe especialmente distinguir entre los dos, pero me gustaría manejar ambos.
La respuesta de Trevor Sullivan a continuación menciona que puedes usar Set-PSBreakpoint -Command Write-Error -Action { break; };
Set-PSBreakpoint -Command Write-Error -Action { break; };
Que parece funcionar bien para atrapar esos casos. Sin embargo, estoy descubriendo que en muchos casos es en realidad un comando que lanza una excepción que me gustaría interrumpir. La respuesta de Roman Kuzmin parece funcionar si configuras $ErrorActionPreference = "stop"
, sin embargo, tengo el problema de que no puedo avanzar en el programa, parece salir de esa ubicación y terminar la secuencia de comandos. Si $ErrorActionPreference = "continue"
no funciona para mí. En general, las trampas parecen tener un problema similar: se escapan de cualquier ámbito anidado, lo que no se desea.
Kinda hokey pero trabaja en declaraciones de error de escritura o de lanzamiento:
$error.clear()
$global:errcnt=0
Set-PSBreakpoint -Command * -action {if ($error.count -ne $global:errcnt) {$global:errcnt=$error.count;break}}
Podría crear una función que establezca un punto de interrupción en una variable y luego cambie el valor de la variable.
function Debug-Here {
if(!(Get-PSBreakpoint -Variable DebugHereCount)) {
$SCRIPT:DebugHere= Set-PSBreakpoint -Variable DebugHereCount
}
$DebugHereCount++
}
Luego, llame a la función desde una captura en la parte superior de la secuencia de comandos para detectar todos los errores de terminación.
trap { Debug-Here }
O llámalo desde una declaración try-catch
.
Get-Item DoesNotExist.txt -ErrorAction Stop
try {
$x = 0
$y = 1/$x
}
catch {
Debug-Here
}
Una vez que esté en el modo de depuración, verifique $Error[0]
para ver qué activó el punto de interrupción.
Por supuesto. Puede crear puntos de interrupción condicionales en PowerShell, usando el cmdlet Set-PSBreakpoint
. Considere el siguiente código. Guárdelo como un archivo de script y ejecútelo. Hay comentarios en línea para ayudarte a entender lo que está pasando.
Tenga en cuenta que hay tres tipos diferentes de puntos de interrupción:
- Línea
- Variable
- Mando
Punto de interrupción del comando
Este ejemplo de código utiliza el tipo de punto de interrupción del comando, porque le digo que solo establezca puntos de interrupción en Get-WmiObject
comandos Get-WmiObject
. Alternativamente, puede especificar un número de línea específico o un tipo de punto de interrupción variable. Utilice el parámetro -Action
para especificar las condiciones bajo las cuales desea que se establezca el punto de interrupción. Debe usar la palabra clave break
algún lugar dentro de ScriptBlock
para indicar al depurador que detenga la ejecución del script de PowerShell.
# 1. Reset $Error to $null
$WmiError = $null;
# 2. Clean up any existing breakpoints
Get-PSBreakpoint | Remove-PSBreakpoint;
# 3. Set breakpoint, but only on Get-WmiObject commands, when the $WmiError variable is not $null
Set-PSBreakpoint -Command Get-WmiObject -Action { if ($WmiError) { break; } };
# 4. Failed Get-WmiObject command
Get-WmiObject -Class Win32_NonExistentClass -ErrorVariable WmiError;
# 5. Successful Get-WmiObject command
# PowerShell breaks here, because:
# - It''s a Get-WmiObject command
# - The $WmiError variable is not null
Get-WmiObject -Class Win32_BIOS;
Como mencionó el uso de Write-Error
, podría establecer un PSBreakpoint
en las líneas donde aparece Write-Error
. Aquí hay un ejemplo de cómo hacer eso:
Set-PSBreakpoint -Command Write-Error -Action { break; };
Bastante fácil, ¿verdad?
Punto de ruptura variable
Este ejemplo utiliza el tipo de variable PSBreakpoint
, pero solo cuando se modifican los contenidos de la variable. Puede usar el parámetro -Mode
para determinar bajo qué condiciones se -Mode
el punto de interrupción variable:
- Leer
- Leer escribir
- Escribir
Código:
# 1. Clean up any existing breakpoints
Get-PSBreakpoint | Remove-PSBreakpoint;
# 2. Set a PSBreakpoint of type "variable" on a variable named "Data," but only when it has changed
Set-PSBreakpoint -Action { Write-Host -ForegroundColor Green -Object (''The $Data variable has changed! Value is: {0}'' -f $Data); break; } -Variable Data -Mode Write;
# 3. No break on this line, because we are not changing the variable
Write-Host -Object $Data;
# 4. Execution is paused on this line, because we change the variable
$Data = 1;
Punto de ruptura de línea
Ahora que hemos examinado los tipos de variable y comando PSBreakpoint
, el último tipo de punto de interrupción para explorar es el punto de interrupción de línea . Si tuviera que copiar / pegar el siguiente código, guardarlo y ejecutarlo, vería que el código se rompe en la línea Write-Host (que es la línea 9), pero solo cuando la propiedad Name
del $Service
variable es igual a WinRM
. Eso es lo que define la declaración condicional en el ScriptBlock
del parámetro -Action
.
# 1. Clean up any existing breakpoints
Get-PSBreakpoint | Remove-PSBreakpoint;
# 2. Set a PSBreakpoint of type "line" on line #8, but only if the $Service variable''s Name property equals ''winrm''
Set-PSBreakpoint -Action { if ($Service.Name -eq ''winrm'') { break; } } -Line 9 -Script $MyInvocation.MyCommand.Path;
# 3. Get a list of Windows Services and iterate over them
foreach ($Service in (Get-WmiObject -Class Win32_Service)) {
Write-Host -Object (''Service name is: {0}'' -f $Service.Name);
}
Sí, hay una forma sencilla de entrar en el depurador en caso de errores. Parece que cada vez que ocurre un error, la variable StackTrace
se actualiza. Entonces uso este truco: en mi perfil tengo estas dos funciones (interruptores):
<#
.Synopsis
Sets $StackTrace breakpoint
.Link
rbps
#>
function sbps {
$null = Set-PSBreakpoint -Variable StackTrace -Mode Write
}
<#
.Synopsis
Removes $StackTrace breakpoint
.Link
sbps
#>
function rbps {
Get-PSBreakpoint -Variable StackTrace | Remove-PSBreakpoint
}
Cuando se llama al primero, se habilita el ingreso al depurador en caso de error. En los errores, StackTrace
se escribe y esto activa el punto de interrupción.
La segunda función desactiva la interrupción de errores en el depurador.
En la mayoría de los casos, este enfoque funciona bien para mí.
ACTUALIZAR
Para usar esta técnica sin funciones de perfil, se puede usar un script auxiliar como Debug-Error.ps1 . Idealmente, debería estar ubicado en la ruta, de modo que los comandos Debug-Error
y Debug-Error -Off
estén siempre disponibles.
Véase también esta entrada de blog .
Algunas entradas relacionadas en Connect: