Part 10. PowerShell as a programming language (Handling errors when executing commands)

26 July 2023 16 minutes Author: Lady Liberty

Robust error handling in PowerShell: Ensuring application security and stability

Error handling when executing commands in PowerShell is an important aspect of programming that allows you to ensure the stability and reliability of script and command execution. When developing programs and scripts in PowerShell, various errors can occur, such as incorrect input data, missing file access, or errors in the code itself. For effective error handling, PowerShell uses try-catch constructs that allow you to catch errors and perform appropriate actions to handle them. Thanks to these constructs, programmers can predict possible error scenarios and take appropriate measures to avoid or correct them. In addition to try-catch constructs, PowerShell also has the ability to use the -ErrorAction command, which allows you to configure the response to errors when commands are executed.

Thanks to this command, programmers can set different levels of error handling: from ignoring to completely stopping the execution of the script in the event of an error. Error handling when executing commands in PowerShell is a key aspect that helps ensure the security and reliability of applications and scripts. The ability to efficiently handle errors allows programmers to more precisely control the execution of the code and ensure more stable operation of the program under different conditions. Thus, error handling in PowerShell is an important tool for programmers to help ensure the stability and reliability of programs and scripts. The use of try-catch constructs and the -ErrorAction command allows you to interact more effectively with emerging errors and ensure the smooth and safe operation of programs in the PowerShell environment.

Handling command execution errors

When using scripts to solve real administrative tasks (managing user accounts, backing up information on servers, analyzing event logs to detect unauthorized access attempts, etc.), the problem of handling possible errors or exceptions becomes relevant. You need to be sure that a certain task is completed in full, and in case of an error, analyze and eliminate it. PowerShell offers several error handling mechanisms, which we will discuss in more detail in this section.

When an error occurs when executing a PowerShe11 command, it not only causes text messages to be displayed on the screen, but also automatically generates a real object whose properties contain full information about this error.

In addition, one aspect of error handling in PowerShell distinguishes this system from other programming languages: errors here can be critical (interrupt command execution) and non-critical (if they occur, command execution continues). This is because PowerShell uses a command pipeline model to process objects. When a non-fatal error occurs, the error information is written to the corresponding object, and the current command continues to process the objects coming to it through the pipeline. A similar error can occur, for example, when using a cmdlet to copy many files, one of which is occupied by another application. If an important operation is being performed, it may make sense to stop it after the first error occurs (then such an error will be critical). However, the same error should be considered critical in one situation and non-critical in another (PowerShell has a mechanism that allows you to explicitly expose these error types).

When non-fatal errors occur, information about them is placed in objects of the ErrorRecord type, which are written to a special error stream. Let’s consider how these objects can be accessed and what information can be extracted from them.

ErrorRecord Object and error stream

In PowerShell, information about errors that occur is written to an error stream that is displayed on the screen by default, for example:

PS С:\Users\andrv> dir "Несуществующий каталог"
dir : Не удается найти путь "С: \Users\andrv\HecyujecTByioiuw4 каталог", т. к.
он не существует.
строка:1 знак:1
+ dir "Несуществующий каталог"
4- Categoryinfo : ObjectNotFound: (С: \Users\andrv\HecymecTByioiip™
каталог:String) [Get-Childltem],
ItemNotFoundException
+ FullyQualifiedErrorld : PathNotFound,Microsoft.PowerShell.Commands.
GetChi1ditemCommand

PowerShell’s GetChildItemCorrmand has an internal number of 2, this stream can be redirected to a text file with a special 2> operator, for example:

PS С:\Users\andrv> dir "Несуществующий каталог" 2> err.txt
Проверим теперь содержимое файла err.txt:
PS С:\Users\andrv> Get-Content .\err.txt
dir : He удается найти путь "С: \Users\andrv\HecyiuecTByKWw каталог", т. к.
он не существует.
строка:1 знак:1
+ dir "Несуществующий каталог" 2> err.txt
+ —----------- ---
+ Categoryinfo : ObjectNotFound: (С:\Users\andrv\HecyiuecTByKwra
каталог:String) [Get-Childltem] ,
11emNоt FoundExcept ion
+ FullyQualifiedErrorld : PathNotFound,Microsoft.PowerShell.Commands.
GetChildltemCommand

As you can see, an error message is written in the err.txt file. The ErrorRecord object, which is created when an error occurs, can be stored in a variable. This is done using a statement that redirects the error stream to the standard output stream (which has an internal number of 1). Example:

PS С: \Users\andrv> $err = dir "Несуществующий каталог" 2>&1
PS С:\Users\andrv> $err.getType().fullName
System.Management.Automation.ErrorRecord
Проверим с помощью командлета Get-Member, какие свойства имеет объект типа
ErrorRecord:
PS С:\Users\andrv> $err | Get-Member -Type Property
TypeName: System.Management.Automation.ErrorRecord
Name MemberType Definition
Categoryinfo
ErrorDetails
Exception
Property
Property
Property
FullyQualifiedErrorld Property
Invocationinfo Property
Pipelinelterationlnfo Property
ScriptStackTrace
Targetobject
Property
Property
System.Management.Automation.Er...
System.Management.Automation.Er...
System.Exception Exception {get...
System.String FullyQualifiedErr...
System.Management.Automation.In...
System.Collections.ObjectModel....
string ScriptStackTrace {get;}
System.Object Targetobject {get...

These properties are related to errors that occur, a description of the most important of them is given in the table. 10.1

Let’s check the contents of the $egg variable:

PS С:\Users\andrv> $err | Format-List * -Force
в System.Management.Automation.SessionStatelnternal.GetChildltems(String
path, Boolean recurse, UInt32 depth, CmdletProviderContext context
в Microsoft.PowerShell.Commands.GetChildltemCommand.ProcessRecord()
writeErrorStream
PSMessageDetails
Exception
: True
: System.Management.Automation.ItemNotFoundException:
He удается найти путь ”C:\Users\andrv\HecymecTByroiunn
каталог", т. к. он не существует.
Гпава 10. Обработка ошибок при выполнении команд 209
Targetobject
Categoryinfo
: С: \Users\andrv\HecynjecTByKWM каталог
: ObjectNotFound: (C: \Users\andrv\HecyuiecTByKwift
каталог:String) [Get-Childltem], ItemNotFoundException
FullyQualifiedErrorld : PathNotFound,
Microsoft.PowerShell.Commands.GetChildltemCommand
ErrorDetails
Invocationinfo
ScriptStackTrace
: System.Management.Automation.Invocationinfo
: в <ScriptBlock>, <Нет файла>: строка 1
Pipelinelterationlnfo : {0, 1}

As you can see, in our example, the error belongs to the PathNotFound category, the TargetObj ect property contains the full path that was used by the dir cmdlet to find the directory or file. The cause of the error was ItemNotFoundException. Let’s find out what the InvocationInfo property contains:

PS С:\Users\andrv> $err.Invocationinfo
MyCommand
BoundParameters
UnboundArguments
ScriptLineNumber
OffsetlnLine
Historyld
ScriptName
Line
PositionMessage
: Get-Childltem
: {}
: {}
: 1
: 8
: 1
: $err = dir "Несуществующий каталог" 2>&1
: строка:1 знак:8
t $err = dir "Несуществующий каталог" 2>&1
+ --------—------------ ----- -------
PSScriptRoot
PSCommandPath
InvocationName
PipelineLength
PipelinePosition
Expectinglnput
Commandorigin
: dir
: 0
: 0
: False
: Internal
DisplayScriptPosition :

The ScriptName property is not populated here because the command was entered directly in the shell’s interactive mode, not from a script. So, we studied the structure of the ErrorRecord object and learned what information can be extracted from its properties. Recall that we obtained this object by redirecting the error stream to the standard output stream in one particular command. It is clear that we will not be able to repeat these manipulations for each command that is executed (the standard output stream may be needed for other purposes) – a more convenient mechanism is needed that allows you to determine whether an error has occurred AND refer to the ErrorRecord object THAT corresponds to THAT OR other mistakes. Similar mechanisms are implemented in PowerShell using several- 210 Part II. PowerShell as a programming language has special variables and parameters, which we will now turn to.

Saving objects that correspond to errors

PowerShell has a special variable $error that contains a set (array) of ErrorRecord objects that correspond to errors that occurred in the current session. The maximum number of elements in this collection is set by the value of the variable $MaximumErrorCount (256 by default):

PS С:\Users\andrv> SMaximumErrorCount
256

After the $error array is filled, the objects for the new errors will replace the objects corresponding to the old errors. The occurrence of each new error leads to the displacement of the elements in the $error array: the object for the last error is hidden in the first element ($error, the object for the previous error is in the second element ($error [1)”), and so on. Let’s repeat the error from the previous section:

PS С:\Users\andrv> dir "Несуществуквдии каталог"
dir : Не удается найти путь "С: \Users\andrv\HecymecTByioiuHH каталог", т. к.
он не существует.
строка:1 знак:1
+ dir "Несуществующий каталог"
+ ~---- ~~----------- ~---
+ CategoryInfo : ObjectNotFound: (С: \Users\andrv\HecynjecTByioiw4
каталог:String) [Get-Chi ldltem],
ItemNotFoundException
+ FullyQualifiedErrorld : PathNotFound,Microsoft.PowerShell.Commands.
GetChildltemCommand

Now verify that the $error[0] element is of type ErrorRecord and output the contents of that element:

PS С:\Users\andrv> $error[0].getType().fullName
System.Management.Automation.ErrorRecord
PS C:\Users\andrv> $error[0]
dir : He удается найти путь "C:\Users\andrv\HecymecTByKWW каталог", т. к.
он не существует.
строка:1 знак:1
+ dir "Несуществующий каталог"
+ Categoryinfo : ObjectNotFound: (С: \Users\andrv\HecymecTByioiW4
каталог:String) [Get-Chi ldltem],
ItemNotFoundException
+ FullyQualifiedErrorld : PathNotFound,Microsoft.PowerShell.Commands.
GetChildltemCommand
Естественно, МЫ можем обращаться КО всем свойствам объекта ErrorRecord:
PS С:\Users\andrv> $error[0].exception
He удается найти путь "С: \Users\andrv\HecyiijecTByKWM каталог", т. к.
он не существует.
PS С:\Users\andrv> $error[0].invocationinfo
MyCommand
BoundParameters
UnboundArguments
ScriptLineNumber
OffsetlnLine
Historyld
ScriptName
Line
PositionMessage
PSScriptRoot
PSCommandPath
InvocationName
PipelineLength
PipelinePosition
Expectinglnput
Commandorigin
DisplayScriptPosition
Get-Childltem
{}
{}
1
1
10
dir "Несуществующий каталог"
строка:1 знак:1
+ dir "Несуществующий каталог"
dir
0
0
False
Internal
PS С:\Users\andrv> $error[0].targetobject
C: \Users\andrv\HecyujecTByKWM каталог

To catch errors for a specific command without redirecting errors to the standard output stream, you can use the standard -Errorvariable option that is defined in all PowerShell commands. For example, in the following command, information about errors will be written to the variable $errs (note that when specifying the value of the parameter -Errorvariable, you do not need to specify the $ sign before the name of the variable):

PS С:\Users\andrv> dir "Каталог 1", "Каталог 2" -Errorvariable errs
dir : Не удается найти путь "С:\Users\andrv\KaTanor 1", т. к. он не существует,
строка:1 знак:1
+ dir "Каталог 1", "Каталог 2" -Errorvariable errs
+ Categoryinfo : ObjectNotFound: (С:\изегз\апЬгу\Каталог 1:String)
[Get-Childltem], Item NotFoundException
+ FullyQualifiedErrorld : PathNotFound,Microsoft.PowerShell.Commands.
GetChi1d11emCommand
dir : He удается найти путь "С: \иэегз\апЬ^\Каталог 2", т. к. он не существует,
строка:! знак:1
+ dir "Каталог 1", "Каталог 2" -ErrorVariable errs
4- Categoryinfo : ObjectNotFound: (С:\Users\andrv\Kaтaлoг 2:String)
[Get-Childltem], Item NotFoundException
+ FullyQualifiedErrorld : PathNotFound,Microsoft.PowerShell.Commands.
GetChi1dItemCommand
При выполнении данной команды возникают две некритические ошибки, т. е.
переменная $errs должна быть массивом, содержащим два объекта ErrorRecord.
Проверим это:
С:\Users\andrv> $errs.count
2
С:\Users\andrv> $errs[O].getType().fullName
System.Management.Automation.ErrorRecord
C:\Users\andrv> $errs[l].getType().fullName
Systern.Management.Automation.ErrorRecord
C:\Users\andrv> $errs[0].targetobject
C:\Users\andrv\KaTanor 1
C:\Users\andrv> $errs[l].targetobject
C:\Users\andrv\Kaтaлoг 2

As you can see, when the Errorvariable parameter is specified, error messages that occur are still displayed on the screen. To suppress these messages, redirect the error stream to an empty $nuii device, for example:

PS С:\Users\andrv> dir "Каталог 1", "Каталог 2" -ErrorVariable errs 2> $null
С:\Users\andrv>
На экран ничего не выводится, а в переменную $errs по-прежнему записываются
два объекта:
PS С:\Users\andrv> $errs.count
2
При этом объекты, соответствующие ошибкам, записываются и в переменную
$еггог:
PS С:\Users\andrv> $error[0]
dir : Не удается найти путь "С:\Users\andrv\Kaтaлoг 2", т. к. он не существует,
строка:1 знак:1
+ dir "Каталог 1", "Каталог 2" -ErrorVariable errs 2> $null
+ —,— —----- ---- _ ----------------- _____
+ Categoryinfo : ObjectNotFound: (С:\Users\andrv\Kaтaлoг 2:String)
[Get-Childltem], Item NotFoundException
+ FullyQualifiedErrorld : PathNotFound,Microsoft.PowerShell.Commands.
GetChildltemCommand
PS C:\Users\andrv> $error[l]
dir : He удается найти путь "С:\Users\andrv\Kaтaлoг 1", т. к. он не существует,
строка:! знак:1
+ dir "Каталог 1", "Каталог 2" -Errorvariable errs 2> $null
+ -- -------------------- -—~~~~
+ Categoryinfo : ObjectNotFound: (С:\Users\andrv\Kaтaлoг 1:String)
[Get-Childltem], Item NotFoundException
+ FullyQualifiedErrorld : PathNotFound,Microsoft.PowerShell.Commands.
GetChildltemCommand

Error monitoring

While working interactively in the PowerShell shell, we get an error message on the screen. After that, we can analyze the $error[0] object and find out all the information about the error. But how do you know about an error inside a script? It turns out that PowerShell has a boolean variable $?, which is True if the last operation completed successfully, and False if any errors occurred during the last operation. For example, if you execute the Get-item cmdlet for an existing directory, the value of the variable $? will be True:

PS С:\Users\andrv> Get-Item С:\
Каталог:
Mode LastWriteTime Length Name
d—hs- 01.08.2021 12:57 C:\
PS C:\Users\andrv> $?
True
Если же выполнить командлет Get-item для несуществующего каталога, то значение переменной $? будет равно False:
PS С:\Users\andrv> Get-Item С:\АБВГГ
Get-Item : Не удается найти путь "С:\АБВГГ", т. к. он не существует.
строка:1 знак:1
+ Get-Item С:\АБВГГ
+ Categoryinfo : ObjectNotFound: (С:\АБВГГ:String) [Get-Item],
ItemNotFoundException
+ FullyQualifiedErrorld : PathNotFound,Microsoft.PowerShell.Commands.
Get I temCommand
PS C:\Users\andrv> $?
False

The concept of return code is defined for Windows external commands and PowerShell scripts (recall that for PowerShell scripts this code can be set using the Exit instruction). In the operating system, the return code of the last command is available through the environment variable %errorlevel%; in the PowerShell shell, this return code is stored in the special variable $lastexitcode. At the same time, if the return code is zero, then the variable $? is given a value of True. If the return code is not equal to zero, then it is considered that an error occurred during the execution of this command and the variable $? is set to False. As an example, let’s execute the command of the cmd.exe interpreter, which sets a zero return code. To do this, you can run cmd.exe with the /c key (execute the command and terminate the interpreter) and specify the exit 0 command to execute:

PS С:\Users\andrv> cmd /с exit О
Проверим значения переменных $lastexitcode и $?:
PS С:\Users\andrv> $LASTEXITCODE
0
PS С:\Users\andrv> $?
True
Теперь выполним команду интерпретатора cmd.exe с ненулевым кодом возврата
(пусть, например, код возврата равен 10):
PS С:\Users\andrv> cmd /с exit 10
Вновь проверим переменные $lastexitcode и $?:
PS С:\Users\andrv> $?
False
PS С:\Users\andrv> $LASTEXITCODE
10
Итак, мы убедились, что переменными $lastexitcode и $? можно пользоваться для
обнаружения ошибок при выполнении сценариев PowerShell.

Error handling modes

At the beginning of this chapter, we talked about the fact that errors in PowerShell are divided into critical (interrupt the execution of the command) and non-critical (if they occur, the execution of the command continues). At the same time, depending on the situation, it may sometimes be necessary to treat non-critical errors as critical and vice versa. A similar error type change is implemented in PowerShell using different error handling modes (Table 10.2).

To set the preferred error handling mode, assign the appropriate value to the $ErrorActionPreference variable, for example: $ErrorActionPreference ” Si1ent1yContinue “

In this case, the continue mode without issuing an error message will affect all commands.

Table 10.2. Error handling modes in PowerShell

If you want to switch the error handling mode for only one cmdlet, when calling it, you must specify the corresponding character constant as the value of the -ErrorAction global parameter (or ea for short), for example:

PS С:\Users\andrv> Get-Item "несуществующий файл" -ErrorAction SilentlyContinue

Handling critical errors (exceptions)

Recall that critical errors in PowerShell are errors that cause the command to stop executing. In traditional programming languages, such errors are usually called exceptions. In PowerShell, you can catch critical errors, that is, perform certain actions when they occur, using two statements: Trap and Try/Catch/Fina11y.

Trapping instructions

The Trap operator was implemented in the first version of PowerShell and was then the only exception handling tool.

There are two possible syntax options for Trap:

trap [тип_исключения] {блок_кода}
ИЛИ
trap {блок_кода}

In the first case, the block of code will be executed only when an exception of a certain type occurs (for example, to catch errors that occur when dividing by zero, you need to specify DivideByZeroException as the exception type), and in the second – when any exception occurs. Let’s give an example.

PS С:\Users\andrv> trap {"Исключение перехвачено!"} $п=0; 1/$п; "п=$п"
Исключение перехвачено!
Попытка деления на нуль.
строка:1 знак:40
+ trap {"Исключение перехвачено!"} $п=0; 1/$п; "п=$п"
+ ~~~~
+ Categoryinfo : NotSpecified: (:) [], RuntimeException
+ FullyQualifiedErrorld : RuntimeException
n=0

In this case, when the expression 1/$n is calculated, an exception occurs (an attempt to divide by zero) and control is transferred to the body of the Trap statement, where the line Exception catchht! is output. Make sure that the object corresponding to the error is written in the $error array (that is why the standard error message is displayed on the tayuke screen):

PS С:\Users\andrv> $error[0]
Попытка деления на нуль.
строка:1 знак:40
+ trap {"Исключение перехвачено!"} $n=0; l/$n; "п=$п"
+ --- -
+ Categoryinfo : NotSpecified: (:) [], RuntimeException
+ FullyQualifiedErrorld : RuntimeException

Note that after the execution of the body of the Trap statement in our example, control is transferred to the command that follows the statement that caused the error. In fact, this is not always the case. If you exit the engine using the Break statement, other commands will not be executed:

PS С:\Users\andrv> trap {"Исключение перехвачено!"; break} $п=0; 1/$п; "п=$п"
Исключение перехвачено!
Попытка деления на нуль.
строка:1 знак:47
+ trap {’’Исключение перехвачено!”; break} $n=0; l/$n; "п=$п"
+
+ Categoryinfo : NotSpecified: (:) [],
ParentContainsErrorRecordException
+ FullyQualifiedErrorld : RuntimeException
Как видим, здесь отсутствует вывод на экран строки ”п=0”. Если выход из обработчика исключения производится С ПОМОЩЬЮ инструкции Continue, то оставшиеся
команды выполняются:
PS С:\Users\andrv> trap {"Исключение перехвачено!"; $_.Exception ; continue}
$n=0; l/$n; "n=$n"
Исключение перехвачено!
Попытка деления на нуль.
п=0

This example also shows that when handling an exception, the corresponding object is accessed as usual using the $ variable. If you exit the exception handler in the usual way (without using the Break or Continu statements), the subsequent behavior is determined by the value of the $ErrorActionPreference variable discussed in the previous section. For example, the following example aborts command execution after handling an exception:

PS С:\Users\andrv> $ErrorActronPreference = "Stop"
PS C:\Users\andrv> trap {"Исключение перехвачено!"} $n=0; l/$n; "n=$n"
Исключение перехвачено!
Попытка деления на нуль.
строка:1 знак:40
+ trap {’’Исключение перехвачено!”} $n=0; l/$n; "п=$п”
-I- Categoryinfo : NotSpecified: (:) [],
ParentContainsErrorRecordException
+ FullyQualifiedErrorld : RuntimeException
Изменим теперь режим обработки ошибок и еще раз выполним те же команды
после инструкции Trap:
PS С:\Users\andrv> $ErrorActionPreference = "SilentlyContinue"
PS C:\Users\andrv> trap {"Исключение перехвачено!"} $n=0; l/$n; "n=$n"
Исключение перехвачено!
n=0
В данном случае стандартное сообщение об ошибке на экран не выводится и после
обработки исключения выполняется следующая команда.

Try/Catch/Finally instruction

The Try/Catch/Finally statement, which has analogues in many programming languages, has been supported since the second version of PowerShell. This is a more convenient and familiar variant of exception handling than the Trap instruction. The Try block contains commands and expressions for which we want to catch critical errors. At least one Catch OR Finally block must correspond to this block. There CAN be SEVERAL Catch BLOCKS, THEY can check for the types of exceptions to be handled. If the Finally block is specified, then the code in it will be executed in any case, regardless of the occurrence of an exception. Usually, finally code is placed to release used resources (closing the database connection, clearing the remote access session, etc.).

Here is an example of handling an exception without checking its type:

PS С:\Users\andrv> try {
» 1
» 2
» 3/0
» 4
» } catch {
» "Ошибка: $_"
» } finally {
» 'Завершающий блок'
» }
1
2
Ошибка: Попытка деления на нуль.
Завершающий блок

In the Try block here, an error occurs when trying to divide the number by zero, and control is transferred to the Catch block, where the error object is available under the name $. After the Catch block is executed, commands from the Fina11y block are executed.

Results

  • There are two types of errors in PowerShell: critical, which interrupts the execution of the command, and non-critical, where the command continues.

  • When an error occurs, the system generates an ErrorRecord object that contains information about the error.

  • Objects with error information are routed to the standard error stream, the contents of which are displayed on the screen by default.

  • ErrorRecord objects that correspond to errors occurring in the current session are automatically stored in a special $error array. The object for the last error is contained in $error [0].

  • Objects corresponding to errors can be stored in other variables by specifying their names in the -ErrorVariab1e standard parameter.

  • In the variable $? The system saves the completion status of the last command. A: The return code of the utility or the external console script is written to the $LASTEXITCODE variable.

  • PowerShell supports several command error handling modes that can be toggled using the $ErrorActionPreference variable and the -ErrorAction global option.

  • Critical errors (exceptions) can be handled using Trap and Try/Catch/Fina11y statements.

Thanks to our team of volunteers for providing information from open sources.

Other related articles
Found an error?
If you find an error, take a screenshot and send it to the bot.