Подтвердить что ты не робот

Есть ли способ ввести отладчик при ошибке?

Есть ли способ войти в отладчик PowerShell в ответ на ошибку? Параметр ErrorAction имеет несколько значений, но я не вижу ничего подобного Debug. Я хотел бы открыть отладчик, как если бы он установил точку останова, но только когда произошла ошибка (от, скажем, Write-Error).

Edit Я должен немного уточнить: я в первую очередь разработчик С# и несколько новичок в PowerShell, я ожидаю, что поведение похоже на то, что отладчик Visual Studio дает вам для "необработанных исключений". Похоже, что для команд PowerShell чаще всего возникают исключения, в то время как пользовательские скрипты, как правило, используют Write-Error. Я не думаю, что мне особенно важно различать их, но я бы хотел обработать оба.

Тревор Салливан отвечает ниже, что вы можете использовать Set-PSBreakpoint -Command Write-Error -Action { break; };, который, как представляется, хорошо работает, чтобы поймать эти случаи. Хотя, я нахожу, что во многих случаях это на самом деле команда, бросающая исключение, которое я хотел бы разбить. Ответ Roman Kuzmin, похоже, работает, если вы установили $ErrorActionPreference = "stop", однако у меня есть проблема, что я не могу пройти через программу, она, кажется, выходит из этого места и заканчивает script. Если $ErrorActionPreference = "continue", это не работает для меня. Ловушки в целом, похоже, имеют аналогичную проблему, из-за которой они вырываются из любых вложенных областей, что нежелательно.

4b9b3361

Ответ 1

Конечно. Вы можете создать условные точки останова в PowerShell с помощью командлета Set-PSBreakpoint. Рассмотрим следующий код. Сохраните его как файл script и выполните его. Есть встроенные комментарии, которые помогут вам понять, что происходит.

Имейте в виду, что существуют три разных типа точек останова:

  • Линия
  • Переменная
  • Command

Командная точка останова

В этом примере кода используется тип контрольной точки команды, потому что я говорю, что он только устанавливает точки останова в командах Get-WmiObject. Вы можете поочередно указать конкретный номер строки или тип переменной контрольной точки. Вы используете параметр -Action, чтобы указать условия, в которых вы хотите установить точку останова. Вы должны использовать ключевое слово break где-то внутри -Action ScriptBlock, чтобы дать отладчику возможность приостановить выполнение PowerShell script.

# 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 a Get-WmiObject command
#     - The $WmiError variable is not null
Get-WmiObject -Class Win32_BIOS;

Поскольку вы упомянули использование Write-Error, вы можете установить PSBreakpoint в строках, где появляется Write-Error. Вот пример того, как это сделать:

Set-PSBreakpoint -Command Write-Error -Action { break; };

Довольно легко, правильно?

Переменная точка останова

В этом примере используется тип переменной PSBreakpoint, но только при изменении содержимого переменной. Вы можете использовать параметр -Mode, чтобы определить, при каких условиях нажата переменная точка останова:

  • Read
  • ReadWrite
  • Запись

код:

# 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;

Линейная точка останова

Теперь, когда мы рассмотрели типы variable и PSBreakpoint, последний тип контрольной точки для проверки - это строка точки останова. Если вам нужно скопировать/вставить код ниже, сохраните его и запустите, вы увидите, что код разбивается на строку Write-Host (которая является строкой 9), но только тогда, когда свойство Name $Service переменная равна WinRM. Именно это определяет условный оператор в параметре -Action ScriptBlock.

# 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 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);
}

Ответ 2

Да, есть простой способ проникнуть в отладчик при ошибках. Это выглядит как каждый раз, когда возникает ошибка, переменная StackTrace обновляется. Так что я используйте этот трюк: в моем профиле у меня есть эти две функции (переключатели):

<#
.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
}

Когда вызывается первое, тогда вхождение в отладчик на ошибках на самом деле включен. На ошибках StackTrace записывается, и это вызывает точку останова.

Вторая функция превращается в отладчик при ошибках.

В большинстве случаев этот подход работает для меня очень хорошо.


UPDATE

Чтобы использовать этот метод без функций профиля, вспомогательный script как Debug-Error.ps1 может быть использован. В идеале он должен располагаться на пути, так что команды Debug-Error и Debug-Error -Off всегда доступны.

Смотрите также этот пост в блоге.


Несколько связанных билетов на Connect:

Ответ 3

Вы можете создать функцию, которая устанавливает точку останова для переменной, а затем изменяет значение переменной.

function Debug-Here {
    if(!(Get-PSBreakpoint -Variable DebugHereCount)) {
        $SCRIPT:DebugHere= Set-PSBreakpoint -Variable DebugHereCount
    }
    $DebugHereCount++
}

Затем вызовите функцию из ловушки a в верхнюю часть script, чтобы поймать все завершающие ошибки.

trap { Debug-Here }

Или вызовите его из инструкции try-catch.

Get-Item DoesNotExist.txt -ErrorAction Stop

try {
    $x = 0
    $y = 1/$x
}
catch {
    Debug-Here
}

Как только вы находитесь в режиме отладки, проверьте $Error[0], чтобы узнать, что вызвало точку останова.

Ответ 4

Kinda hokey, но работает над ошибками write-error или throw:

$error.clear()
$global:errcnt=0
Set-PSBreakpoint -Command * -action {if ($error.count -ne $global:errcnt) {$global:errcnt=$error.count;break}}