пятница, декабря 28, 2012

Бойся Try/Catch…

Вот, только что наткнулся: конструкция try/catch в PS v.2 (которая с Шарепойнтом 2010 только и работает) срабатывает в зависимости от типа ошибки - некоторые вовсе не перехватывает.

Пример: конструкция try{2/0}catch{"низя!"} вываливает сообщение в консоль:

clip_image001

PS v.3 исполняет как надо:

clip_image002

Выясняется, правда, что если делить не на нуль, а на переменную, содержащую нуль, то этот примерчик работает и во второй версии:

clip_image003

С другой стороны, наткнулся-то я на такое смешное поведение Catch в реальном скрипте при попытке в блоке Try записать данные в системный лог:

Write-EventLog -LogName $eventLogName -Source $eventLogSourceName ...

Если переменная $eventLogSourceName пустая, то в Catch ловится ошибка с сообщением "Cannot validate argument on parameter 'Source'. The argument is null or empty."

А если там неверное значение, то исключение не ловится и сообщение пишется в консоль:

clip_image004

Как с этим бороться? Не перечислять же все известные в природе исключения? Тем более, что некоторые всё равно не перехватываются…

Нашёлся способ в виде конструкции Trap - нечто наподобие On Error в бейсике.

Ловит все ошибки, но особо с действиями над этими ошибками не развернёшься. В файл на диске ошибки записать можно (чего я, собственно, и добивался), а вот собрать тексты разных ошибок, чтобы потом их разом записать в лог, как обычно в CS делаю - вигвам.

Вот примерчик:

$err="Error: "

trap$err += $_
$err
Out-File -FilePath "errors.log" -Encoding "utf8" -Append -InputObject $_.Exception.Message
continue }

$dvdr = 0
1/$dvdr
NonsenseString 
2+3
$err #здесь та же строка "Error: " :(

Здесь выполняются все действия, а сообщения об ошибках выводятся в файл:


Попытка деления на нуль.
Имя "NonsenseString" не распознано как имя командлета, функции, файла скрипта или выполняемой программы.


Не фонтан, конечно, но, всё же, лучше, чем просто пропадающее сообщение (пускай даже код после "кривых" Catch иногда и выполняется нормально)…

5 комментариев:

Анонимный комментирует...

На форуме по powershell так объяснили из-за чего catch не срабатывает:
"The example here uses 1/$null. The reason for doing this instead of simply
1/0 is because the PowerShell interpreter does something called constant
expression folding.

It looks at expressions that contain only constant values. When it sees one,
it evaluates that expression once at compile time so it doesn’t have to waste
time doing it again at runtime.

This means that impossible expressions, such as division by zero, are caught and treated as
parsing errors. Parsing errors can’t be caught and don’t get logged when they’re entered
interactively, so they don’t make for a good example. (If one script calls another script and that
script has one of these errors, the calling script can catch it, but the script being parsed cannot.)"

DkmS комментирует...

C нулём, конечно, пример искусственный (хотя для единообразия могли бы и сделать нормальное исключение), а что там говорят про другие случаи?
Я тут натолкнулся на то, что не ловится попытка удалить несуществующий файл (при помощи Remove-Item)...

Анонимный комментирует...

Есть два вида ошибок: terminating errors и non-terminating errors.
Try/catch ловит только terminating errors.
Есть два варианта, чтобы exception не вываливался:
1) отключить выдачу exception при выполнении команды:
Remove-Item "c:\example.docx" -ea 0
2)конвертнуть non-terminating error в terminating error - нужно установить параметр ErrorAction с значением stop.
Например:
try { Remove-Item "c:\example.docx" -ea stop } catch { "Test" }

Анонимный комментирует...

Есть два вида ошибок: terminating errors и non-terminating errors.
Try/catch ловит только terminating errors.
Есть два варианта, чтобы exception не вываливался:
1) отключить выдачу exception при выполнении команды:
Remove-Item "c:\example.docx" -ea 0
2)конвертнуть non-terminating error в terminating error - нужно установить параметр ErrorAction с значением stop.
Например:
try { Remove-Item "c:\example.docx" -ea stop } catch { "Test" }

DkmS комментирует...

Спасибо, Александр - полезно про ошибки, буду использовать.
Но зачем же так, через универсальный интерфейс, сделано...