I’ve been looking for a way to terminate a PowerShell (PS1) script when an unrecoverable error occurs within a function. For example:
function foo() {
# Do stuff that causes an error
$host.Exit()
}
Of course there’s no such thing as $host.Exit()
. There is $host.SetShouldExit()
, but this actually closes the console window, which is not what I want. What I need is something equivalent to Python’s sys.exit()
that will simply stop execution of the current script without further ado.
Edit: Yeah, it’s just exit
.
GChuf
1,1351 gold badge17 silver badges28 bronze badges
asked Jan 7, 2010 at 17:42
2
I realize this is an old post but I find myself coming back to this thread a lot as it is one of the top search results when searching for this topic. However, I always leave more confused then when I came due to the conflicting information. Ultimately I always have to perform my own tests to figure it out. So this time I will post my findings.
TL;DR Most people will want to use Exit
to terminate a running scripts. However, if your script is merely declaring functions to later be used in a shell, then you will want to use Return
in the definitions of said functions.
Exit vs Return vs Break
-
Exit: This will «exit» the currently running context. If you call this command from a script it will exit the script. If you call this command from the shell it will exit the shell.
If a function calls the Exit command it will exit what ever context it is running in. So if that function is only called from within a running script it will exit that script. However, if your script merely declares the function so that it can be used from the current shell and you run that function from the shell, it will exit the shell because the shell is the context in which the function contianing the
Exit
command is running.Note: By default if you right click on a script to run it in PowerShell, once the script is done running, PowerShell will close automatically. This has nothing to do with the
Exit
command or anything else in your script. It is just a default PowerShell behavior for scripts being ran using this specific method of running a script. The same is true for batch files and the Command Line window. -
Return: This will return to the previous call point. If you call this command from a script (outside any functions) it will return to the shell. If you call this command from the shell it will return to the shell (which is the previous call point for a single command ran from the shell). If you call this command from a function it will return to where ever the function was called from.
Execution of any commands after the call point that it is returned to will continue from that point. If a script is called from the shell and it contains the
Return
command outside any functions then when it returns to the shell there are no more commands to run thus making aReturn
used in this way essentially the same asExit
. -
Break: This will break out of loops and switch cases. If you call this command while not in a loop or switch case it will break out of the script. If you call
Break
inside a loop that is nested inside a loop it will only break out of the loop it was called in.There is also an interesting feature of
Break
where you can prefix a loop with a label and then you can break out of that labeled loop even if theBreak
command is called within several nested groups within that labeled loop.While ($true) { # Code here will run :myLabel While ($true) { # Code here will run While ($true) { # Code here will run While ($true) { # Code here will run Break myLabel # Code here will not run } # Code here will not run } # Code here will not run } # Code here will run }
answered May 16, 2014 at 19:19
New GuyNew Guy
8,6461 gold badge15 silver badges12 bronze badges
8
Exit
will exit PowerShell too. If you wish to «break» out of just the current function or script — use Break
If ($Breakout -eq $true)
{
Write-Host "Break Out!"
Break
}
ElseIf ($Breakout -eq $false)
{
Write-Host "No Breakout for you!"
}
Else
{
Write-Host "Breakout wasn't defined..."
}
answered Oct 20, 2012 at 2:56
2
Write-Error is for non-terminating errors and throw is for terminating errors
The Write-Error cmdlet declares a non-terminating error. By default,
errors are sent in the error stream to the host program to be
displayed, along with output.Non-terminating errors write an error to the error stream, but
they do not stop command processing. If a non-terminating error is
declared on one item in a collection of input items, the command
continues to process the other items in the collection.To declare a
terminating error, use the Throw keyword. For more information, see
about_Throw (http://go.microsoft.com/fwlink/?LinkID=145153).
answered Dec 12, 2013 at 23:44
Greg BrayGreg Bray
14.9k12 gold badges81 silver badges104 bronze badges
3
Throwing an exception will be good especially if you want to clarify the error reason:
throw "Error Message"
This will generate a terminating error.
answered Oct 31, 2014 at 9:24
Amr BahaaAmr Bahaa
1,0879 silver badges9 bronze badges
6
I think you are looking for Return
instead of Break
. Break is typically used for loops and only breaks from the innermost code block. Use Return to exit a function or script.
answered May 6, 2013 at 10:48
RobRob
3,4181 gold badge19 silver badges27 bronze badges
3
I coincidentally found out that Break <UnknownLabel>
(e.g. simply Break Script
, where the label Script
doesn’t exists) appears to break out of the entire script (even from within a function) and keeps the host alive.
This way you could create a function that breaks the script from anywhere (e.g. a recursive loop) without knowing the current scope (and creating labels):
Function Quit($Text) {
Write-Host "Quiting because: " $Text
Break Script
}
answered Jul 11, 2017 at 9:04
iRoniRon
20.6k10 gold badges53 silver badges80 bronze badges
1
May be it is better to use «trap». A PowerShell trap specifies a codeblock to run when a terminating or error occurs. Type
Get-Help about_trap
to learn more about the trap statement.
answered Jan 9, 2010 at 16:13
I thought up a neat little trick to do just that, when I wanted a function to exit a script without throwing an error, but not exit the console if the function was used from there. It involves the $MyInvocation
automatic variable’s CommandOrigin
property.
if ( $MyInvocation.CommandOrigin -eq 'Internal' ) { exit }
If the function is ran from the console, the CommandOrigin will be Runspace.
answered Dec 17, 2021 at 7:56
VopelVopel
6616 silver badges11 bronze badges
0
I used this for a reruning of a program. I don’t know if it would help, but it is a simple if statement requiring only two different entry’s. It worked in powershell for me.
$rerun = Read-Host "Rerun report (y/n)?"
if($rerun -eq "y") { Show-MemoryReport }
if($rerun -eq "n") { Exit }
Don’t know if this helps, but i believe this would be along the lines of terminating a program after you have run it. However in this case, every defined input requires a listed and categorized output. You could also have the exit call up a new prompt line and terminate the program that way.
answered Dec 5, 2015 at 17:36
I’ve been looking for a way to terminate a PowerShell (PS1) script when an unrecoverable error occurs within a function. For example:
function foo() {
# Do stuff that causes an error
$host.Exit()
}
Of course there’s no such thing as $host.Exit()
. There is $host.SetShouldExit()
, but this actually closes the console window, which is not what I want. What I need is something equivalent to Python’s sys.exit()
that will simply stop execution of the current script without further ado.
Edit: Yeah, it’s just exit
.
GChuf
1,1351 gold badge17 silver badges28 bronze badges
asked Jan 7, 2010 at 17:42
2
I realize this is an old post but I find myself coming back to this thread a lot as it is one of the top search results when searching for this topic. However, I always leave more confused then when I came due to the conflicting information. Ultimately I always have to perform my own tests to figure it out. So this time I will post my findings.
TL;DR Most people will want to use Exit
to terminate a running scripts. However, if your script is merely declaring functions to later be used in a shell, then you will want to use Return
in the definitions of said functions.
Exit vs Return vs Break
-
Exit: This will «exit» the currently running context. If you call this command from a script it will exit the script. If you call this command from the shell it will exit the shell.
If a function calls the Exit command it will exit what ever context it is running in. So if that function is only called from within a running script it will exit that script. However, if your script merely declares the function so that it can be used from the current shell and you run that function from the shell, it will exit the shell because the shell is the context in which the function contianing the
Exit
command is running.Note: By default if you right click on a script to run it in PowerShell, once the script is done running, PowerShell will close automatically. This has nothing to do with the
Exit
command or anything else in your script. It is just a default PowerShell behavior for scripts being ran using this specific method of running a script. The same is true for batch files and the Command Line window. -
Return: This will return to the previous call point. If you call this command from a script (outside any functions) it will return to the shell. If you call this command from the shell it will return to the shell (which is the previous call point for a single command ran from the shell). If you call this command from a function it will return to where ever the function was called from.
Execution of any commands after the call point that it is returned to will continue from that point. If a script is called from the shell and it contains the
Return
command outside any functions then when it returns to the shell there are no more commands to run thus making aReturn
used in this way essentially the same asExit
. -
Break: This will break out of loops and switch cases. If you call this command while not in a loop or switch case it will break out of the script. If you call
Break
inside a loop that is nested inside a loop it will only break out of the loop it was called in.There is also an interesting feature of
Break
where you can prefix a loop with a label and then you can break out of that labeled loop even if theBreak
command is called within several nested groups within that labeled loop.While ($true) { # Code here will run :myLabel While ($true) { # Code here will run While ($true) { # Code here will run While ($true) { # Code here will run Break myLabel # Code here will not run } # Code here will not run } # Code here will not run } # Code here will run }
answered May 16, 2014 at 19:19
New GuyNew Guy
8,6461 gold badge15 silver badges12 bronze badges
8
Exit
will exit PowerShell too. If you wish to «break» out of just the current function or script — use Break
If ($Breakout -eq $true)
{
Write-Host "Break Out!"
Break
}
ElseIf ($Breakout -eq $false)
{
Write-Host "No Breakout for you!"
}
Else
{
Write-Host "Breakout wasn't defined..."
}
answered Oct 20, 2012 at 2:56
2
Write-Error is for non-terminating errors and throw is for terminating errors
The Write-Error cmdlet declares a non-terminating error. By default,
errors are sent in the error stream to the host program to be
displayed, along with output.Non-terminating errors write an error to the error stream, but
they do not stop command processing. If a non-terminating error is
declared on one item in a collection of input items, the command
continues to process the other items in the collection.To declare a
terminating error, use the Throw keyword. For more information, see
about_Throw (http://go.microsoft.com/fwlink/?LinkID=145153).
answered Dec 12, 2013 at 23:44
Greg BrayGreg Bray
14.9k12 gold badges81 silver badges104 bronze badges
3
Throwing an exception will be good especially if you want to clarify the error reason:
throw "Error Message"
This will generate a terminating error.
answered Oct 31, 2014 at 9:24
Amr BahaaAmr Bahaa
1,0879 silver badges9 bronze badges
6
I think you are looking for Return
instead of Break
. Break is typically used for loops and only breaks from the innermost code block. Use Return to exit a function or script.
answered May 6, 2013 at 10:48
RobRob
3,4181 gold badge19 silver badges27 bronze badges
3
I coincidentally found out that Break <UnknownLabel>
(e.g. simply Break Script
, where the label Script
doesn’t exists) appears to break out of the entire script (even from within a function) and keeps the host alive.
This way you could create a function that breaks the script from anywhere (e.g. a recursive loop) without knowing the current scope (and creating labels):
Function Quit($Text) {
Write-Host "Quiting because: " $Text
Break Script
}
answered Jul 11, 2017 at 9:04
iRoniRon
20.6k10 gold badges53 silver badges80 bronze badges
1
May be it is better to use «trap». A PowerShell trap specifies a codeblock to run when a terminating or error occurs. Type
Get-Help about_trap
to learn more about the trap statement.
answered Jan 9, 2010 at 16:13
I thought up a neat little trick to do just that, when I wanted a function to exit a script without throwing an error, but not exit the console if the function was used from there. It involves the $MyInvocation
automatic variable’s CommandOrigin
property.
if ( $MyInvocation.CommandOrigin -eq 'Internal' ) { exit }
If the function is ran from the console, the CommandOrigin will be Runspace.
answered Dec 17, 2021 at 7:56
VopelVopel
6616 silver badges11 bronze badges
0
I used this for a reruning of a program. I don’t know if it would help, but it is a simple if statement requiring only two different entry’s. It worked in powershell for me.
$rerun = Read-Host "Rerun report (y/n)?"
if($rerun -eq "y") { Show-MemoryReport }
if($rerun -eq "n") { Exit }
Don’t know if this helps, but i believe this would be along the lines of terminating a program after you have run it. However in this case, every defined input requires a listed and categorized output. You could also have the exit call up a new prompt line and terminate the program that way.
answered Dec 5, 2015 at 17:36
В Powershell существует несколько уровней ошибок и несколько способов их обработать. Проблемы одного уровня (Non-Terminating Errors) можно решить с помощью привычных для Powershell команд. Другой уровень ошибок (Terminating Errors) решается с помощью исключений (Exceptions) стандартного, для большинства языков, блока в виде Try, Catch и Finally.
Как Powershell обрабатывает ошибки
До рассмотрения основных методов посмотрим на теоретическую часть.
Автоматические переменные $Error
В Powershell существует множество переменных, которые создаются автоматически. Одна из таких переменных — $Error хранит в себе все ошибки за текущий сеанс PS. Например так я выведу количество ошибок и их сообщение за весь сеанс:
Get-TestTest
$Error
$Error.Count
При отсутствии каких либо ошибок мы бы получили пустой ответ, а счетчик будет равняться 0:
Переменная $Error являет массивом и мы можем по нему пройтись или обратиться по индексу что бы найти нужную ошибку:
$Error[0]
foreach ($item in $Error){$item}
Свойства объекта $Error
Так же как и все что создается в Powershell переменная $Error так же имеет свойства (дополнительную информацию) и методы. Названия свойств и методов можно увидеть через команду Get-Member:
$Error | Get-Member
Например, с помощью свойства InvocationInfo, мы можем вывести более структурный отчет об ошибки:
$Error[0].InvocationInfo
Методы объекта $Error
Например мы можем очистить логи ошибок используя clear:
$Error.clear()
Критические ошибки (Terminating Errors)
Критические (завершающие) ошибки останавливают работу скрипта. Например это может быть ошибка в названии командлета или параметра. В следующем примере команда должна была бы вернуть процессы «svchost» дважды, но из-за использования несуществующего параметра ‘—Error’ не выполнится вообще:
'svchost','svchost' | % {Get-Process -Name $PSItem} --Error
Не критические ошибки (Non-Terminating Errors)
Не критические (не завершающие) ошибки не остановят работу скрипта полностью, но могут вывести сообщение об этом. Это могут быть ошибки не в самих командлетах Powershell, а в значениях, которые вы используете. На предыдущем примере мы можем допустить опечатку в названии процессов, но команда все равно продолжит работу:
'svchost111','svchost' | % {Get-Process -Name $PSItem}
Как видно у нас появилась информация о проблеме с первым процессом ‘svchost111’, так как его не существует. Обычный процесс ‘svchost’ он у нас вывелся корректно.
Параметр ErrorVariable
Если вы не хотите использовать автоматическую переменную $Error, то сможете определять свою переменную индивидуально для каждой команды. Эта переменная определяется в параметре ErrorVariable:
'svchost111','svchost' | % {Get-Process -Name $PSItem } -ErrorVariable my_err_var
$my_err_var
Переменная будет иметь те же свойства, что и автоматическая:
$my_err_var.InvocationInfo
Обработка некритических ошибок
У нас есть два способа определения последующих действий при ‘Non-Terminating Errors’. Это правило можно задать локально и глобально (в рамках сессии). Мы сможем полностью остановить работу скрипта или вообще отменить вывод ошибок.
Приоритет ошибок с $ErrorActionPreference
Еще одна встроенная переменная в Powershell $ErrorActionPreference глобально определяет что должно случится, если у нас появится обычная ошибка. По умолчанию это значение равно ‘Continue’, что значит «вывести информацию об ошибке и продолжить работу»:
$ErrorActionPreference
Если мы поменяем значение этой переменной на ‘Stop’, то поведение скриптов и команд будет аналогично критичным ошибкам. Вы можете убедиться в этом на прошлом скрипте с неверным именем процесса:
$ErrorActionPreference = 'Stop'
'svchost111','svchost' | % {Get-Process -Name $PSItem}
Т.е. скрипт был остановлен в самом начале. Значение переменной будет храниться до момента завершения сессии Powershell. При перезагрузке компьютера, например, вернется значение по умолчанию.
Ниже значение, которые мы можем установить в переменной $ErrorActionPreference:
- Continue — вывод ошибки и продолжение работы;
- Inquire — приостановит работу скрипта и спросит о дальнейших действиях;
- SilentlyContinue — скрипт продолжит свою работу без вывода ошибок;
- Stop — остановка скрипта при первой ошибке.
Самый частый параметр, который мне приходится использовать — SilentlyContinue:
$ErrorActionPreference = 'SilentlyContinue'
'svchost111','svchost' | % {Get-Process -Name $PSItem}
Использование параметра ErrorAction
Переменная $ErrorActionPreference указывает глобальный приоритет, но мы можем определить такую логику в рамках команды с параметром ErrorAction. Этот параметр имеет больший приоритет чем $ErrorActionPreference. В следующем примере, глобальная переменная определяет полную остановку скрипта, а в параметр ErrorAction говорит «не выводить ошибок и продолжить работу»:
$ErrorActionPreference = 'Stop'
'svchost111','svchost' | % {Get-Process -Name $PSItem -ErrorAction 'SilentlyContinue'}
Кроме ‘SilentlyContinue’ мы можем указывать те же параметры, что и в переменной $ErrorActionPreference.
Значение Stop, в обоих случаях, делает ошибку критической.
Обработка критических ошибок и исключений с Try, Catch и Finally
Когда мы ожидаем получить какую-то ошибку и добавить логику нужно использовать Try и Catch. Например, если в вариантах выше мы определяли нужно ли нам отображать ошибку или останавливать скрипт, то теперь сможем изменить выполнение скрипта или команды вообще. Блок Try и Catch работает только с критическими ошибками и в случаях если $ErrorActionPreference или ErrorAction имеют значение Stop.
Например, если с помощью Powershell мы пытаемся подключиться к множеству компьютеров один из них может быть выключен — это приведет к ошибке. Так как эту ситуацию мы можем предвидеть, то мы можем обработать ее. Процесс обработки ошибок называется исключением (Exception).
Синтаксис и логика работы команды следующая:
try {
# Пытаемся подключиться к компьютеру
}
catch [Имя исключения 1],[Имя исключения 2]{
# Раз компьютер не доступен, сделать то-то
}
finally {
# Блок, который выполняется в любом случае последним
}
Блок try мониторит ошибки и если она произойдет, то она добавится в переменную $Error и скрипт перейдет к блоку Catch. Так как ошибки могут быть разные (нет доступа, нет сети, блокирует правило фаервола и т.д.) то мы можем прописывать один блок Try и несколько Catch:
try {
# Пытаемся подключится
}
catch ['Нет сети']['Блокирует фаервол']{
# Записываем в файл
}
catch ['Нет прав на подключение']{
# Подключаемся под другим пользователем
}
Сам блок finally — не обязательный и используется редко. Он выполняется самым последним, после try и catch и не имеет каких-то условий.
Catch для всех типов исключений
Как и было показано выше мы можем использовать блок Catch для конкретного типа ошибок, например при проблемах с доступом. Если в этом месте ничего не указывать — в этом блоке будут обрабатываться все варианты ошибок:
try {
'svchost111','svchost' | % {Get-Process -Name $PSItem -ErrorAction 'Stop'}
}
catch {
Write-Host "Какая-то неисправность" -ForegroundColor RED
}
Такой подход не рекомендуется использовать часто, так как вы можете пропустить что-то важное.
Мы можем вывести в блоке catch текст ошибки используя $PSItem.Exception:
try {
'svchost111','svchost' | % {Get-Process -Name $PSItem -ErrorAction 'Stop'}
}
catch {
Write-Host "Какая-то неисправность" -ForegroundColor RED
$PSItem.Exception
}
Переменная $PSItem хранит информацию о текущей ошибке, а глобальная переменная $Error будет хранит информацию обо всех ошибках. Так, например, я выведу одну и ту же информацию:
$Error[0].Exception
Создание отдельных исключений
Что бы обработать отдельную ошибку сначала нужно найти ее имя. Это имя можно увидеть при получении свойств и методов у значения переменной $Error:
$Error[0].Exception | Get-Member
Так же сработает и в блоке Catch с $PSItem:
Для вывода только имени можно использовать свойство FullName:
$Error[0].Exception.GetType().FullName
Далее, это имя, мы вставляем в блок Catch:
try {
'svchost111','svchost' | % {Get-Process -Name $PSItem -ErrorAction 'Stop'}
}
catch [Microsoft.PowerShell.Commands.ProcessCommandException]{
Write-Host "Произошла ошибка" -ForegroundColor RED
$PSItem.Exception
}
Так же, как и было описано выше мы можем усложнять эти блоки как угодно указывая множество исключений в одном catch.
Выброс своих исключений
Иногда нужно создать свои собственные исключения. Например мы можем запретить добавлять через какой-то скрипт названия содержащие маленькие буквы или сотрудников без указания возраста и т.д. Способов создать такие ошибки — два и они тоже делятся на критические и обычные.
Выброс с throw
Throw — выбрасывает ошибку, которая останавливает работу скрипта. Этот тип ошибок относится к критическим. Например мы можем указать только текст для дополнительной информации:
$name = 'AD.1'
if ($name -match '.'){
throw 'Запрещено использовать точки в названиях'
}
Если нужно, то мы можем использовать исключения, которые уже были созданы в Powershell:
$name = 'AD.1'
if ($name -like '*.*'){
throw [System.IO.FileNotFoundException]'Запрещено использовать точки в названиях'
}
Использование Write-Error
Команда Write-Error работает так же, как и ключ ErrorAction. Мы можем просто отобразить какую-то ошибку и продолжить выполнение скрипта:
$names = @('CL1', 'AD.1', 'CL3')
foreach ($name in $names){
if ($name -like '*.*'){
Write-Error -Message 'Обычная ошибка'
}
else{
$name
}
}
При необходимости мы можем использовать параметр ErrorAction. Значения этого параметра были описаны выше. Мы можем указать значение ‘Stop’, что полностью остановит выполнение скрипта:
$names = @('CL1', 'AD.1', 'CL3')
foreach ($name in $names){
if ($name -like '*.*'){
Write-Error -Message 'Обычная ошибка' -ErrorAction 'Stop'
}
else{
$name
}
}
Отличие команды Write-Error с ключом ErrorAction от обычных команд в том, что мы можем указывать исключения в параметре Exception:
Write-Error -Message 'Обычная ошибка' -ErrorAction 'Stop'
Write-Error -Message 'Исключение' -Exception [System.IO.FileNotFoundException] -ErrorAction 'Stop'
В Exception мы так же можем указывать сообщение. При этом оно будет отображаться в переменной $Error:
Write-Error -Exception [System.IO.FileNotFoundException]'Моё сообщение'
…
Теги:
#powershell
#ошибки
Продолжаем тему обработки ошибок в PowerShell, начатую в предыдущей статье. Сегодня речь пойдет об обработке прерывающих ошибок (исключений). Надо понимать, что сообщение об ошибке — это не то же самое, что исключение. Как вы помните, в PowerShell есть два типа ошибок — непрерывающие и прерывающие.
Непрерывающие ошибки позволяют продолжить работу, тогда как прерывающие останавливают выполнение команды. Остановка приводит к исключению (exception), которое и можно отлавливать и обрабатывать.
Примечание. Таким же образом можно обрабатывать и непрерывающие ошибки. Изменение параметра ErrorAction на Stop прервет выполнение команды и произведет исключение, которое можно уловить.
Для наглядности сгенерируем ошибку, попытавшись прочитать файл, на который у нас нет прав. А теперь обратимся к переменной $Error и выведем данные об исключении. Как видите, данное исключение имеет тип UnauthorizedAccessException и относится к базовому типу System.SystemException.
Для обработки исключений в PowerShell есть несколько способов, которые мы сегодня и рассмотрим.
Try/Catch/Finally
Конструкция Try/Catch/Finally предназначена для обработки исключений, возникающих в процессе выполнения скрипта. В блоке Try располагается исполняемый код, в котором должны отслеживаться ошибки. При возникновении в блоке Try прерывающей ошибки оболочка PowerShell ищет соответствующий блок Catch для обработки этой ошибки, и если он найден, то выполняет находящиеся в нем инструкции. Блок Catch может включать в себя любые команды, необходимые для обработки возникнувшей ошибки и\или восстановления дальнейшей работы скрипта.
Блок Finally располагается обычно в конце скрипта. Команды, находящиеся в нем, выполняются в любом случае, независимо от возникновения ошибок. Была ли ошибка перехвачена и обработана блоком Catch или при выполнении скрипта ошибок не возникало вообще, блок Finally будет выполнен. Присутствие этого блока в скрипте необязательно, основная его задача — высвобождение ресурсов (закрытие процессов, очистка памяти и т.п.).
В качестве примера в блок Try поместим код, который читает файлы из указанной директории. При возникновении проблем блок Catch выводит сообщение об ошибке, после чего отрабатывает блок Finally и работа скрипта завершается:
try {
Get-Content -Path ″C:\Files\*″ -ErrorAction Stop
}
catch {
Write-Host ″Some error was found.″
}
finally {
Write-Host ″Finish.″
}
Для блока Catch можно указать конкретный тип ошибки, добавив после ключевого слова Catch в квадратных скобках название исключения. Так в следующем примере укажем в качестве типа исключение System.UnauthorizedAccessException, которое возникает при отсутствии необходимых прав доступа к объекту:
try {
Get-Content -Path ″C:\Files\*″ -ErrorAction Stop
}
catch [System.UnauthorizedAccessException]
{
Write-Host ″File is not accessible.″
}
finally {
Write-Host ″Finish.″
}
Если для блока Catch указан тип ошибки, то этот блок будет обрабатывать только этот тип ошибок, или тип ошибок, наследуемый от указанного типа. При возникновении другого типа ошибка не будет обработана. Если же тип не указан, то блок будет обрабатывать любые типы ошибок, возникающие в блоке Try.
Блок Try может включать несколько блоков Catch, для разных типов ошибок, соответственно можно для каждого типа ошибки задать свой обработчик. Например, возьмем два блока Catch, один для ошибки при отсутствии прав доступа, а второй — для любых других ошибок, которые могут возникнуть в процессе выполнения скрипта:
try {
Get-Content -Path ″C:\Files\*″ -ErrorAction Stop
}
catch [System.UnauthorizedAccessException]
{
Write-Host ″File is not accessible.″
}
catch {
Write-Host ″Other type of error was found:″
Write-Host ″Exception type is $($_.Exception.GetType().Name)″
}
finally {
Write-Host ″Finish.″
}
Trap
Еще один способ обработки ошибок — это установка ловушки (trap). В этом варианте обработчик ошибок задается с помощью ключевого слова Trap, определяющего список команд, которые должны выполниться при возникновении прерывающей ошибки и исключения. Когда происходит исключение, то оболочка PowerShell ищет в коде инструкции Trap для данной ошибки, и если находит, то выполняет их.
Возьмем наиболее простой пример ловушки, которая обрабатывает все произошедшие исключения и выводит сообщение об ошибке:
trap {
Write-Host ″Error was found.″
}
Get-Content -Path C:\File\* -ErrorAction Stop
Так же, как и для try\catch, ключевое слово Trap позволяет задавать тип исключения, указав его в квадратных скобках. К примеру, можно задать в скрипте несколько ловушек, одну нацелив на конкретную ошибку, а вторую на обработку оставшихся:
trap [System.Management.Automation.ItemNotFoundException]
{
Write-Host ″File is not accessible.″
break
}
trap {
Write-Host ″Other error was found.″
continue
}
Get-Content -Path C:\File\* -ErrorAction Stop
Вместе с Trap можно использовать ключевые слова Break и Continue, которые позволяют определить, должен ли скрипт продолжать выполняться после возникновения прерывающей ошибки. По умолчанию при возникновении исключения выполняются команды, входящие в блок Trap, выводится информация об ошибке, после чего выполнение скрипта продолжается со строки, вызвавшей ошибку:
trap {
Write-Host ″Error was found.″
}
Write-Host ″Before error.″
Get-Content -Path C:\File\* -ErrorAction Stop
Write-Host ″After error.″
Если использовать ключевое слово Break, то при возникновении ошибки будет выполнены команды в блоке Trap, после чего выполнение скрипта будет прервано:
trap {
Write-Host ″Error was found.″
break
}
Write-Host ″Before error.″
Get-Content -Path C:\File\* -ErrorAction Stop
Write-Host ″After error.″
Как видно из примера, команда, следующая за исключением, не отработала и сообщение ″After error.″ выведено не было.
Если же в Trap включено ключевое слово Continue, то выполнение скрипта будет продолжено, так же как и в случае по умолчанию. Единственное отличие в том, что с Continue ошибка не записывается в поток Error и не выводится на экран.
trap {
Write-Host ″Error was found.″
continue
}
Write-Host ″Before error.″
Get-Content -Path C:\File\* -ErrorAction Stop
Write-Host ″After error.″
Область действия
При отлове ошибок с помощью Trap надо помнить о такой вещи, как область действия (scope). Оболочка PowerShell является глобальной областью, в которую входят все процессы, запущенный скрипт получает собственную область, а если внутри скрипта определены функции, то внутри каждой определена своя, частная область действия. Это создает своего рода родительско-дочернюю иерархию.
При появлении исключения оболочка ищет ловушку в текущей области. Если в ней есть ловушка, она выполняется, если нет — то производится выход в вышестоящую область и поиск ловушки там. Когда ловушка находится, то происходит ее выполнение. Затем, если ловушка предусматривает продолжение работы, то оболочка возобновляет выполнение кода, начиная с той строки, которая вызвала исключение, но при этом оставаясь в той же области, не возвращаясь обратно.
Для примера возьмем функцию Content, внутри которой будет выполняться наш код. Область действия внутри функции назовем scope 1, а снаружи scope 2:
trap {
Write-Host ″Error was found.″
continue
}
function Content {
Write-Host ″Before error, scope1.″
Get-Content -Path C:\File\* -ErrorAction Stop
Write-Host ″After error, scope 1.″
}
Content
Write-Host ″After error, scope 2.″
При возникновении ошибки в функции Content оболочка будет искать ловушку внутри нее. Затем, не найдя ловушку внутри функции, оболочка выйдет из текущей области и будет искать в родительской области. Ловушка там есть, поэтому будет выполнена обработка ошибки, после чего Continue возобновит выполнение скрипта, но уже в родительской области (scope 2), не возвращаясь обратно в функцию (scope 1).
А теперь немного изменим скрипт, поместив ловушку внутрь функции:
function Content {
trap {
Write-Host ″Error was found.″
continue
}
Write-Host ″Before error, scope 1.″
Get-Content -Path C:\File\* -ErrorAction Stop
Write-Host ″After error, scope 1.″
}
Content
Write-Host ″After error, scope 2.″
Как видите, теперь при возникновении ошибки внутри функции будет произведена обработка ошибки, после чего выполнение будет продолжено с той строки, в которой произошла ошибка — не выходя из функции.
Заключение
Если сравнить Try/Catch и Trap, то у каждого метода есть свои достоинства и недостатки. Конструкцию Try/Catch можно более точно нацелить на возможную ошибку, так как Catch обрабатывает только содержимое блока Try. Эту особенность удобно использовать при отладке скриптов, для поиска ошибок.
И наоборот, Trap является глобальным обработчиком, отлавливая ошибки во всем скрипте независимо от расположения. На мой взгляд это более жизненный вариант, который больше подходит для постоянной работы.
Unlike other programming languages, the PowerShell scripting language has two types of error scenarios: terminating and non-terminating. Both of these types of errors are «bad,» but by classifying them differently, the scripter can handle them differently. This distinction is important if you want to stop or exit a PowerShell script when it errors.
Contents
- Non-terminating errors
- Terminating errors
- Turning non-terminating into terminating errors
- Author
- Recent Posts
Adam Bertram is a 20-year IT veteran, Microsoft MVP, blogger, and trainer.
Non-terminating errors
For example, let’s say I’ve got a script that checks to see whether a file exists or not. I’ll call it ErrorExample.ps1. This script uses Test-Path to confirm whether a file exists. If it doesn’t, it will return an error via the Write-Error cmdlet. This error will be non-terminating because Write-Error always returns non-terminating errors.
if (-not (Test-Path -Path C:\DoesNotExist.txt)) { Write-Error 'The file does not exist' } else { Write-Host 'The file does exist' } Write-Host 'Continuing script regardless if file exists or not...'
The script will continue executing code whether or not that file exists as shown below.
Non terminating error
This is a non-terminating error example because Write-Error did not terminate the script at line two. Instead, it returned the error to the console and kept going. This scenario sometimes isn’t desirable. Perhaps that file you’re checking for is critical to the success of the lines below it. In that case, you’d want the script to stop execution completely. You’d know that if that file doesn’t exist, the rest of the script isn’t going to work.
Terminating errors
Terminating errors are the second type of error in PowerShell. We can think of terminating errors as exceptions. Exceptions are either errors that terminate a script completely or ones PowerShell «throws» into a catch block to handle the error and perform whatever necessary actions after throwing the exception.
One way a scripter can invoke a terminating error is by using the throw keyword. This PowerShell construct creates a terminating error while also throwing an exception. Using the example above, let’s say that file is critical and we’d like to stop script execution if it doesn’t exist. We could replace Write-Error with throw instead.
if (-not (Test-Path -Path C:\DoesNotExist.txt)) { throw 'The file does not exist' } else { Write-Host 'The file does exist' } Write-Host 'Continuing script regardless if file exists or not...'
Notice now that line seven doesn’t run; throw has stopped the script at line two.
Terminating error
Turning non-terminating into terminating errors
It’s sometimes hard to replace all Write-Error references and other actions that produce non-terminating errors with throw statements. Instead, you can keep all of those non-terminating error-producing commands in your scripts and «convert» them to terminating errors.
If you’d like to turn all non-terminating errors into terminating ones, you can perform a global action change by setting the $ErrorActionPreference variable to Stop. This automatic variable controls non-terminating error behavior. By setting $ErrorActionPreference to Stop, you’re telling PowerShell to treat all instances where a non-terminating error occurs as terminating. This action applies across the board.
However, maybe you’d rather not change all non-terminating errors to terminating ones and pick certain ones instead. Using the common parameter ErrorAction applied to all cmdlets and advanced functions, you can make this happen.
In our example again, I can actually «override» the Write-Error cmdlet’s behavior by forcing it to throw a terminating error. I’ll use the ErrorAction parameter and set it to Stop.
Subscribe to 4sysops newsletter!
Write-Error 'The file does not exist' -ErrorAction Stop
When the script runs now, you’ll see that it stops execution without using the throw keyword. You can use the ErrorAction parameter on every cmdlet and advanced function in PowerShell. It is a great way to choose which commands should stop script execution and which ones should not. Depending on the severity and dependencies further down in the script, this may or not may be necessary.