Powershell текст ошибки

В Powershell существует несколько уровней ошибок и несколько способов их обработать. Проблемы одного уровня (Non-Terminating Errors) можно решить с помощью привычных для Powershell команд. Другой уровень ошибок (Terminating Errors) решается с помощью исключений (Exceptions) стандартного, для большинства языков, блока в виде Try, Catch и Finally. 

Как Powershell обрабатывает ошибки

До рассмотрения основных методов посмотрим на теоретическую часть.

Автоматические переменные $Error

В Powershell существует множество переменных, которые создаются автоматически. Одна из таких переменных — $Error хранит в себе все ошибки за текущий сеанс PS. Например так я выведу количество ошибок и их сообщение за весь сеанс:

Get-TestTest
$Error
$Error.Count

Переменная $Error в Powershell

При отсутствии каких либо ошибок мы бы получили пустой ответ, а счетчик будет равняться 0:

Счетчик ошибок с переменной $Error в Powershell

Переменная $Error являет массивом и мы можем по нему пройтись или обратиться по индексу что бы найти нужную ошибку:

$Error[0]

foreach ($item in $Error){$item}

Вывод ошибки по индексу в Powershell c $Error

Свойства объекта $Error

Так же как и все что создается в Powershell переменная $Error так же имеет свойства (дополнительную информацию) и методы. Названия свойств и методов можно увидеть через команду Get-Member:

$Error | Get-Member

Свойства переменной $Error в Powershell

Например, с помощью свойства InvocationInfo, мы можем вывести более структурный отчет об ошибки:

$Error[0].InvocationInfo

Детальная информация об ошибке с $Error в Powershell

Методы объекта $Error

Например мы можем очистить логи ошибок используя clear:

$Error.clear()

Очистка логов об ошибке в Powershell с $Error

Критические ошибки (Terminating Errors)

Критические (завершающие) ошибки останавливают работу скрипта. Например это может быть ошибка в названии командлета или параметра. В следующем примере команда должна была бы вернуть процессы «svchost» дважды, но из-за использования несуществующего параметра ‘—Error’ не выполнится вообще:

'svchost','svchost' | % {Get-Process -Name $PSItem} --Error 

Критические ошибки в Powershell Terminating Errors

Не критические ошибки (Non-Terminating Errors)

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

'svchost111','svchost' | % {Get-Process -Name $PSItem}

Не критические ошибки в Powershell Non-Terminating Errors

Как видно у нас появилась информация о проблеме с первым процессом ‘svchost111’, так как его не существует. Обычный процесс ‘svchost’ он у нас вывелся корректно.

Параметр ErrorVariable

Если вы не хотите использовать автоматическую переменную $Error, то сможете определять свою переменную индивидуально для каждой команды. Эта переменная определяется в параметре ErrorVariable:

'svchost111','svchost' | % {Get-Process -Name $PSItem } -ErrorVariable my_err_var
$my_err_var

Использование ErrorVariable в Powershell

Переменная будет иметь те же свойства, что и автоматическая:

$my_err_var.InvocationInfo

Свойства  ErrorVariable в Powershell

Обработка некритических ошибок

У нас есть два способа определения последующих действий при ‘Non-Terminating Errors’. Это правило можно задать локально и глобально (в рамках сессии). Мы сможем полностью остановить работу скрипта или вообще отменить вывод ошибок.

Приоритет ошибок с $ErrorActionPreference

Еще одна встроенная переменная в Powershell $ErrorActionPreference глобально определяет что должно случится, если у нас появится обычная ошибка. По умолчанию это значение равно ‘Continue’, что значит «вывести информацию об ошибке и продолжить работу»:

$ErrorActionPreference

Определение $ErrorActionPreference в Powershell

Если мы поменяем значение этой переменной на ‘Stop’, то поведение скриптов и команд будет аналогично критичным ошибкам. Вы можете убедиться в этом на прошлом скрипте с неверным именем процесса:

$ErrorActionPreference = 'Stop'
'svchost111','svchost' | % {Get-Process -Name $PSItem}

Определение глобальной переменной $ErrorActionPreference в Powershell

Т.е. скрипт был остановлен в самом начале. Значение переменной будет храниться до момента завершения сессии Powershell. При перезагрузке компьютера, например, вернется значение по умолчанию.

Ниже значение, которые мы можем установить в переменной $ErrorActionPreference:

  • Continue — вывод ошибки и продолжение работы;
  • Inquire — приостановит работу скрипта и спросит о дальнейших действиях;
  • SilentlyContinue — скрипт продолжит свою работу без вывода ошибок;
  • Stop — остановка скрипта при первой ошибке.

Самый частый параметр, который мне приходится использовать — SilentlyContinue:

$ErrorActionPreference = 'SilentlyContinue'
'svchost111','svchost' | % {Get-Process -Name $PSItem}

Игнорирование ошибок в Powershell с ErrorActionPreference и SilentlyContinue

Использование параметра ErrorAction

Переменная $ErrorActionPreference указывает глобальный приоритет, но мы можем определить такую логику в рамках команды с параметром ErrorAction. Этот параметр имеет больший приоритет чем $ErrorActionPreference. В следующем примере, глобальная переменная определяет полную остановку скрипта, а в параметр ErrorAction говорит «не выводить ошибок и продолжить работу»:

$ErrorActionPreference = 'Stop'
'svchost111','svchost' | % {Get-Process -Name $PSItem -ErrorAction 'SilentlyContinue'}

Использование параметра ErrorAction в ошибках с Powershell

Кроме ‘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
}

Игнорирование всех ошибок с try и catch в Powershell

Такой подход не рекомендуется использовать часто, так как вы можете пропустить что-то важное.

Мы можем вывести в блоке catch текст ошибки используя $PSItem.Exception:

try {
   'svchost111','svchost' | % {Get-Process -Name $PSItem -ErrorAction 'Stop'}
}
catch {
   Write-Host "Какая-то неисправность" -ForegroundColor RED
   $PSItem.Exception
}

Переменная PSITem в блоке try и catch в Powershell

Переменная $PSItem хранит информацию о текущей ошибке, а глобальная переменная $Error будет хранит информацию обо всех ошибках. Так, например, я выведу одну и ту же информацию:

$Error[0].Exception

Вывод сообщения об ошибке в блоке try и catch в Powershell

Создание отдельных исключений

Что бы обработать отдельную ошибку сначала нужно найти ее имя. Это имя можно увидеть при получении свойств и методов у значения переменной $Error:

$Error[0].Exception | Get-Member

Поиск имени для исключения ошибки в Powershell

Так же сработает и в блоке Catch с $PSItem:

Наименование ошибок для исключений в Powershell

Для вывода только имени можно использовать свойство FullName:

$Error[0].Exception.GetType().FullName

Вывод типа ошибок и их названия в Powershell

Далее, это имя, мы вставляем в блок Catch:

try {
   'svchost111','svchost' | % {Get-Process -Name $PSItem -ErrorAction 'Stop'}
}
catch [Microsoft.PowerShell.Commands.ProcessCommandException]{
   Write-Host "Произошла ошибка" -ForegroundColor RED
   $PSItem.Exception
}

Указываем исключение ошибки в блоке Try Catch Powershell

Так же, как и было описано выше мы можем усложнять эти блоки как угодно указывая множество исключений в одном catch.

Выброс своих исключений

Иногда нужно создать свои собственные исключения. Например мы можем запретить добавлять через какой-то скрипт названия содержащие маленькие буквы или сотрудников без указания возраста и т.д. Способов создать такие ошибки — два и они тоже делятся на критические и обычные.

Выброс с throw

Throw — выбрасывает ошибку, которая останавливает работу скрипта. Этот тип ошибок относится к критическим. Например мы можем указать только текст для дополнительной информации:

$name = 'AD.1'

if ($name -match '.'){
   throw 'Запрещено использовать точки в названиях'
}

Выброс ошибки с throw в Powershell

Если нужно, то мы можем использовать исключения, которые уже были созданы в Powershell:

$name = 'AD.1'

if ($name -like '*.*'){
   throw [System.IO.FileNotFoundException]'Запрещено использовать точки в названиях'
}

Выброс ошибки с throw в Powershell

Использование Write-Error

Команда Write-Error работает так же, как и ключ ErrorAction. Мы можем просто отобразить какую-то ошибку и продолжить выполнение скрипта:

$names = @('CL1', 'AD.1', 'CL3')

foreach ($name in $names){
   if ($name -like '*.*'){
      Write-Error -Message 'Обычная ошибка'
   }
   else{
      $name
   }
}

Использование Write-Error для работы с исключениями в Powershell

При необходимости мы можем использовать параметр ErrorAction. Значения этого параметра были описаны выше. Мы можем указать значение ‘Stop’, что полностью остановит выполнение скрипта:

$names = @('CL1', 'AD.1', 'CL3')

foreach ($name in $names){
   if ($name -like '*.*'){
      Write-Error -Message 'Обычная ошибка' -ErrorAction 'Stop'
   }
   else{
      $name
   }
}

Использование Write-Error и Stop в Powershell

Отличие команды Write-Error с ключом ErrorAction от обычных команд в том, что мы можем указывать исключения в параметре Exception:

Write-Error -Message 'Обычная ошибка' -ErrorAction 'Stop'

Write-Error -Message 'Исключение' -Exception [System.IO.FileNotFoundException] -ErrorAction 'Stop'

Свойства Write-Errror в Powershell

В Exception мы так же можем указывать сообщение. При этом оно будет отображаться в переменной $Error:

Write-Error -Exception [System.IO.FileNotFoundException]'Моё сообщение'

Свойства Write-Errror в Powershell 

Теги:

#powershell

#ошибки

The best way in my opinion to trap errors in PowerShell would be to use the following:

$Error[0].Exception.GetType().FullName

Here is an example of how to use this properly. Basically test what you are trying to do in PowerShell with different scenarios in which your script will fail.

Here is a typical PowerShell error message:

PS C:> Stop-Process -Name 'FakeProcess'
Stop-Process : Cannot find a process with the name "FakeProcess". Verify the process name and call the cmdlet again.
At line:1 char:1
+ Stop-Process -Name 'FakeProcess'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (FakeProcess:String) [Stop-Process], ProcessCommandException
    + FullyQualifiedErrorId : NoProcessFoundForGivenName,Microsoft.PowerShell.Commands.StopProcessCommand

Next you would get the exception of the error message:

PS C:> $Error[0].Exception.GetType().FullName
Microsoft.PowerShell.Commands.ProcessCommandException

You would setup your code to catch the error message as follows:

Try
{
    #-ErrorAction Stop is needed to go to catch statement on error
    Get-Process -Name 'FakeProcess' -ErrorAction Stop
}
Catch [Microsoft.PowerShell.Commands.ProcessCommandException]
{
    Write-Host "ERROR: Process Does Not Exist. Please Check Process Name"
}

Output would look like the following instead of the Powershell standard error in above example:

ERROR: Process Does Not Exist. Please Check Process Name

Lastly, you can also use multiple catch blocks to handle multiple errors in your code. You can also include a «blanket» catch block to catch all errors you haven’t handled. Example:

Try
{
    Get-Process -Name 'FakeProcess' -ErrorAction Stop
}

Catch [Microsoft.PowerShell.Commands.ProcessCommandException]
{
    Write-Host "ERROR: Process Does Not Exist. Please Check Process Name"
}

Catch [System.Exception]
{
    Write-Host "ERROR: Some Error Message Here!"
}

Catch
{
    Write-Host "ERROR: I am a blanket catch to handle all unspecified errors you aren't handling yet!"
}

Error Handling is a very important concept in every programming language including PowerShell which gives us several possibilities to manage errors in code.

In this article, I will try to cover as many as possible error handling concepts with examples from my practice and one of the important concepts is writing errors in external Error Log text file.

Why Should We Bother Handling Errors In PowerShell

It is no fun to run any code or application full of errors and bugs as the matter a fact it is quite annoying so in order for users to have a pleasant experience handling the errors is one of the essentials in programming.

PowerShell is no exception to that way of thinking and programming ethics although it can take some additional coding and effort but trust me it is always worth it.

I would be more than happy to share my experience with error handling and even more satisfied if I can hear your tips from the field so we can all enrich our knowledge on this very important subject.

Here is a general approach to error handling and logging errors into the external text file. I have an example in the next subheading that further explain each step so everything is much more understandable.

  1. Identify which Commands need Error Handling in your Function, CmdLet or Script.
  2. Set the ErrorAction parameter to value Stop for the command that needs Error Handling. This will stop executing the command when an error occurs and it will show the error message. Basically, we are making non-terminating errors into terminating in order to be able to handle the error in the catch block.
  3. Command that has ErrorAction parameter set to value Stop is wrapped in Try { } block. So when Error occurs handling of the code will jump from the Try block to Catch block.
  4. In Catch { } block that immediately follows after Try {} block, the error is handled by writing to the error log file on the disk.
  5. We use our own Write-ErrorLog CmdLet inside Catch{ } block to write the error in a text file on the disk. (See the explanation and code here)
  6. We have an additional switch error parameter to decide whether we want to write an error in the log file or not. This is totally optional.
  7. Use the Finally { } block if needed.
  8. Test the whole setup by intentionally breaking the code while in the development phase.
  9. Since some of the CmdLets calls are Scheduled we have routine to check external Error Log text file at least once a week and investigate errors that are caught. This step is part of improving the overall quality of the written code.

Example Of PowerShell Error Handling

To show you Error Handling and implement previously defined steps I will use my own Get-CPUInfo CmdLet which is in the Common module of the Efficiency Booster PowerShell Project. Efficiency Booster PowerShell Project is a library of CmdLets that help us IT experts in day to day IT tasks.

In order to follow me along, I highly encourage you to download the zip file with the source code used in this example.

Here is the location of Get-CPUInfo script:
…[My] DocumentsWindowsPowerShellModules3common

Get-CPUInfo CmdLet script location

Let’s use steps defined in the previous subheading to this example.

Step 1. I have identified the command that needs Error Handling in Get-CPUInfo CmdLet and that is a call to Get-CimInstance CmdLet.

Get-CimInstance @params 

Step 2. So I have set up the ErrorAction parameter to the value ‘Stop‘ for Get-CimInstance CmdLet in order to force non-terminating errors into terminating and then to be able to handle such errors.

INFO: I use parameter splatting when running CmdLet. If you want to know more about parameter splating please read this article.

            $params = @{ 'ComputerName'=$computer;
                         'Class'='Win32_Processor';
                         'ErrorAction'='Stop'}
            $CPUInfos = Get-CimInstance @params | 
                            Select-Object   @{label="ServerName"; Expression={$_.SystemName}}, 
                                            @{label="CPU"; Expression={$_.Name}}, 
                                            @{label="CPUid"; Expression={$_.DeviceID}}, 
                                            NumberOfCores, 
                                            AddressWidth

Step 3. Wrap up the call to Get-CimInstance CmdLet into the Try Block in order to be able to handle the error in a catch block that follows.

         try {
            Write-Verbose "Start processing: $computer - $env - $logicalname"
            Write-Verbose "Start Win32_Processor processing..."
            $CPUInfos = $null
            $params = @{ 'ComputerName'=$computer;
                         'Class'='Win32_Processor';
                         'ErrorAction'='Stop'}
            $CPUInfos = Get-CimInstance @params | 
                            Select-Object   @{label="ServerName"; Expression={$_.SystemName}}, 
                                            @{label="CPU"; Expression={$_.Name}}, 
                                            @{label="CPUid"; Expression={$_.DeviceID}}, 
                                            NumberOfCores, 
                                            AddressWidth           
            Write-Verbose "Finish Win32_Processor processing..."                    
            foreach ($CPUInfo in $CPUInfos) {
                Write-Verbose "Start processing CPU: $CPUInfo"
                $properties = @{ 'Environment'=$env;
                                 'Logical name'=$logicalname;
                                 'Server name'=$CPUInfo.ServerName;
            	                 'CPU'=$CPUInfo.CPU;
            	                 'CPU ID'=$CPUInfo.CPUid;
            	                 'Number of CPU cores'=$CPUInfo.NumberOfCores; 
                                 '64 or 32 bits'=$CPUInfo.AddressWidth;
                                 'IP'=$ip;
                                 'Collected'=(Get-Date -UFormat %Y.%m.%d' '%H:%M:%S)}
                $obj = New-Object -TypeName PSObject -Property $properties
                $obj.PSObject.TypeNames.Insert(0,'Report.CPUInfo')
                Write-Output $obj
                Write-Verbose "Finish processing CPU: $CPUInfo"
            }
 Write-Verbose "Finish processing: $computer - $env - $logicalname"                       
        }

Step 4. When the error occurs in the try block it is handled in the Catch Block.
It is important to notice following in the catch block of code:

  • Get-CPUInfo CmdLet switch parameter $errorlog has been used to decide whether to log the errors in an external text file or not. This is completely optional.
  • Certain Error properties are collected using an automatic variable $_ ($PSItem is another name for the same variable). If you want to know more about which properties we collect please read here.
  • Collected data about the error that will be handled has been passed to another CmdLet Write-ErrorLog that will write the data in an external text log file. Please read here about Write-ErrorLog CmdLet.
catch {
            Write-Warning "Computer failed: $computer - $env - $logicalname CPU failed: $CPUInfos"
            Write-Warning "Error message: $_"
            if ( $errorlog ) {
                $errormsg = $_.ToString()
                $exception = $_.Exception
                $stacktrace = $_.ScriptStackTrace
                $failingline = $_.InvocationInfo.Line
                $positionmsg = $_.InvocationInfo.PositionMessage
                $pscommandpath = $_.InvocationInfo.PSCommandPath
                $failinglinenumber = $_.InvocationInfo.ScriptLineNumber
                $scriptname = $_.InvocationInfo.ScriptName
                Write-Verbose "Start writing to Error log."
                Write-ErrorLog -hostname $computer -env $env -logicalname $logicalname -errormsg $errormsg -exception $exception -scriptname $scriptname -failinglinenumber $failinglinenumber -failingline $failingline -pscommandpath $pscommandpath -positionmsg $pscommandpath -stacktrace $stacktrace
                Write-Verbose "Finish writing to Error log."
            }
        }

Step 5. I have already mentioned that Write-ErrorLog CmdLet has been used to write the error data into an external text log file. Read more about this CmdLet here.

Step 6. I did not need Finally { } block for this example.

Step 7. In the development phase, I was intentionally breaking Get-CPUInfo CmdLet to test my error handling code.

Step 8. If Get-CPUInfo CmdLet is part of the scheduled code I would look regularly Error log file and work on correcting all the bugs in the code produced by Get-CPUInfo CmdLet.

Chain Of Events When PowerShell Error Occurs

Let’s talk a little bit about what is happening when an error occurs in PowerShell and which events are triggered one after another.

  • Call to CmdLet is failing and error has just occurred.
  • Since we have ErrorAction parameter set to value Stop our non-terminating error has forced into terminating error so the execution of code stops.
  • The error that is failing in the CmdLet is written in the $Error automatic variable by the PowerShell.
  • We have forced the error to be terminating in order to be able to handle with try catch finally block of error handling.
  • Since our CmdLet call is wrapped in Try block and error is terminating PowerShell can trigger error handling looking for a Catch block.
  • We can have several Catch blocks for one try block. If we have a Catch block that handles the actual error number that block is executed.
  • Otherwise, PowerShell will look Catch block that handles all error numbers.
  • Optionally in the Catch block, we can have code that will write the error in the external text Error log file.
  • We can use the automatic variable $Error or $_ object to read Error data and write them in the external file.
  • If there is no Catch block PowerShell will look for Catch block in parent call if we have nested calls.
  • If there are no further Catch block or no Catch block at all that will handle error then PowerShell looks for Finally block to execute which is used to clean up resources as needed.
  • After executing Finally block error message will be sent to the error stream for further processing.

In further sections of this article, you can read in more detail about the many terms mentioned (ErrorAction, $Error, Try Catch Finally, Terminating, Non-Terminating, etc ) in this bulleted list in order to better understand them.

How To Write PowerShell Errors Into The External Log File

Here I will explain the code of Write-ErrorLog CmdLet that writes error data that occurs in CmdLets and handle the error data into an external text file.

Error data are written in the file named Error_Log.txt in folder PSlogs.

Location and name of external error text log file
Example of error logged in an external text file

Write-ErrorLog CmdLet is part of the Efficiency Booster PowerShell Project and if you want to download the source code of this CmdLet please click here.

Here is the location of Write-ErrorLog script which is part of the Utils module:
…[My] DocumentsWindowsPowerShellModules2utils

Write-ErrorLog CmdLet script location

Write-ErrorLog CmdLet Code

Here is the code of the whole Write-ErrorLog CmdLet.

<#
.SYNOPSIS
Writes errors that occur in powershell scripts into error log file.
.DESCRIPTION
Writes errors that occur in powershell scripts into error log file.
Error log file and error log folder will be created if doesn't exist.
Error log file name is Error_Log.txt and it has been saved into ..DocumentsPSlogs

.PARAMETER hostname
Name of the computer that is failing.

.PARAMETER env
Environment where computer is located. For example: Production, Acceptance, Test, Course etc.

.PARAMETER logicalname
Type of the server that is failing. For example: Application, Web, Integration, FTP, Scan, etc. 

.PARAMETER errormsg
Error message.

.PARAMETER exception
Error number.

.PARAMETER scriptname
Name of the powershell script that is failing.

.PARAMETER failinglinenumber
Line number in the script that is failing.

.PARAMETER failingline
Content of failing line.

.PARAMETER pscommandpath
Path to the powershell command.

.PARAMETER positionmsg
Error message position.

.PARAMETER stacktrace
Stack trace of the error.

.EXAMPLE
Write-ErrorLog -hostname "Server1" -env "PROD" -logicalname "APP1" -errormsg "Error Message" -exception "HResult 0789343" -scriptname "Test.ps1" -failinglinenumber "25" -failingline "Get-Service" -pscommandpath "Command pathc." -positionmsg "Position message" -stacktrace "Stack trace" 

.EXAMPLE
Help Write-ErrorLog -Full

.LINK 
Out-File
#>
Function Write-ErrorLog {
[CmdletBinding()]
param (

    [Parameter(Mandatory=$false,
                HelpMessage="Error from computer.")] 
    [string]$hostname,

    [Parameter(Mandatory=$false,
                HelpMessage="Environment that failed. (Test, Production, Course, Acceptance...)")] 
    [string]$env,

    [Parameter(Mandatory=$false,
                HelpMessage="Type of server that failed. (Application, Web, Integration...)")] 
    [string]$logicalname,
    
    [Parameter(Mandatory=$false,
                HelpMessage="Error message.")] 
    [string]$errormsg,
    
    [Parameter( Mandatory=$false,
                HelpMessage="Exception.")]
    [string]$exception,
    
    [Parameter(Mandatory=$false, 
                HelpMessage="Name of the script that is failing.")]
    [string]$scriptname,
     
    [Parameter(Mandatory=$false,
                HelpMessage="Script fails at line number.")]
    [string]$failinglinenumber,

    [Parameter(Mandatory=$false,
                HelpMessage="Failing line looks like.")]
    [string]$failingline,
    
    [Parameter(Mandatory=$false,
                HelpMessage="Powershell command path.")]
    [string]$pscommandpath,    

    [Parameter(Mandatory=$false,
                HelpMessage="Position message.")]
    [string]$positionmsg, 

    [Parameter(Mandatory=$false,
                HelpMessage="Stack trace.")]
    [string]$stacktrace
)
BEGIN { 
        
        $errorlogfile = "$homeDocumentsPSlogsError_Log.txt"
        $errorlogfolder = "$homeDocumentsPSlogs"
        
        if  ( !( Test-Path -Path $errorlogfolder -PathType "Container" ) ) {
            
            Write-Verbose "Create error log folder in: $errorlogfolder"
            New-Item -Path $errorlogfolder -ItemType "Container" -ErrorAction Stop
        
            if ( !( Test-Path -Path $errorlogfile -PathType "Leaf" ) ) {
                Write-Verbose "Create error log file in folder $errorlogfolder with name Error_Log.txt"
                New-Item -Path $errorlogfile -ItemType "File" -ErrorAction Stop
            }
        }
}
PROCESS {  

            Write-Verbose "Start writing to Error log file. $errorlogfile"
            $timestamp = Get-Date 
            #IMPORTANT: Read just first value from collection not the whole collection.
            "   " | Out-File $errorlogfile -Append
            "************************************************************************************************************" | Out-File $errorlogfile -Append
            "Error happend at time: $timestamp on a computer: $hostname - $env - $logicalname" | Out-File $errorlogfile -Append
            "Error message: $errormsg" | Out-File $errorlogfile -Append
            "Error exception: $exception" | Out-File $errorlogfile -Append
            "Failing script: $scriptname" | Out-File $errorlogfile -Append
            "Failing at line number: $failinglinenumber" | Out-File $errorlogfile -Append
            "Failing at line: $failingline" | Out-File $errorlogfile -Append
            "Powershell command path: $pscommandpath" | Out-File $errorlogfile -Append
            "Position message: $positionmsg" | Out-File $errorlogfile -Append
            "Stack trace: $stacktrace" | Out-File $errorlogfile -Append
            "------------------------------------------------------------------------------------------------------------" | Out-File $errorlogfile -Append                   
            
            Write-Verbose "Finish writing to Error log file. $errorlogfile"
}        
END { 

}
}
#region Execution examples
#Write-ErrorLog -hostname "Server1" -env "PROD" -logicalname "APP1" -errormsg "Error Message" -exception "HResult 0789343" -scriptname "Test.ps1" -failinglinenumber "25" -failingline "Get-Service" -pscommandpath "Command pathc." -positionmsg "Position message" -stacktrace "Stack trace" -Verbose
#endregion

Write-ErrorLog CmdLet Explained

Let’s make our hand’s a little bit “dirty” and dive into PowerShell code.

In the BEGIN block we:

  • Check if folder PSlogs exist in the (My) Documents folder of the current user.
    • If the PSlogs folder doesn’t exist then create the folder.
  • Check if file Error_Log.txt exists in the folder PSlogs.
    • If Error_Log.txt doesn’t exist then create the file.
  • Now we can move on to PROCESS block code.
BEGIN { 
        
        $errorlogfile = "$homeDocumentsPSlogsError_Log.txt"
        $errorlogfolder = "$homeDocumentsPSlogs"
        
        if  ( !( Test-Path -Path $errorlogfolder -PathType "Container" ) ) {
            
            Write-Verbose "Create error log folder in: $errorlogfolder"
            New-Item -Path $errorlogfolder -ItemType "Container" -ErrorAction Stop
        
            if ( !( Test-Path -Path $errorlogfile -PathType "Leaf" ) ) {
                Write-Verbose "Create error log file in folder $errorlogfolder with name Error_Log.txt"
                New-Item -Path $errorlogfile -ItemType "File" -ErrorAction Stop
            }
        }
}

In the PROCESS block:

  • We format the line of text that we want to write into the log file.
  • Then we pipe formatted text to Out-File CmdLet with the Append parameter to write that line of text in the file.
  • We repeat the process of formatting the line of text and appending of that line to the Error log file.
PROCESS {  

            Write-Verbose "Start writing to Error log file. $errorlogfile"
            $timestamp = Get-Date 
            #IMPORTANT: Read just first value from collection not the whole collection.
            "   " | Out-File $errorlogfile -Append
            "************************************************************************************************************" | Out-File $errorlogfile -Append
            "Error happend at time: $timestamp on a computer: $hostname - $env - $logicalname" | Out-File $errorlogfile -Append
            "Error message: $errormsg" | Out-File $errorlogfile -Append
            "Error exception: $exception" | Out-File $errorlogfile -Append
            "Failing script: $scriptname" | Out-File $errorlogfile -Append
            "Failing at line number: $failinglinenumber" | Out-File $errorlogfile -Append
            "Failing at line: $failingline" | Out-File $errorlogfile -Append
            "Powershell command path: $pscommandpath" | Out-File $errorlogfile -Append
            "Position message: $positionmsg" | Out-File $errorlogfile -Append
            "Stack trace: $stacktrace" | Out-File $errorlogfile -Append
            "------------------------------------------------------------------------------------------------------------" | Out-File $errorlogfile -Append                   
            
            Write-Verbose "Finish writing to Error log file. $errorlogfile"
} 

$PSItem or $_

$PSItem contains the current object in the pipeline object and we use it to read Error properties in this case.

Just a quick explanation of each Error property from the $_ ($PSItem) automatic variable that we collect and write in Logfile:

  • $_.ToString() – This is Error Message.
  • $_.Exception – This is Error Exception.
  • $_.InvocationInfo.ScriptName – This the PowerShell script name where Error occurred.
  • $_.InvocationInfo.ScriptLineNumber – This is line number within the PowerShell script where Error occurred.
  • $_.InvocationInfo.Line – This is the line of code within PowerShell script where Error occurred.
  • $_.InvocationInfo.PSCommandPath – This is the path to the PowerShell script file on the disk.
  • $_.InvocationInfo.PositionMessage – This is a formatted message indicating where the CmdLet appeared in the line.
  • $_.ScriptStackTrace – This is the Trace of the Stack.

As you can see on the screenshot below we collect really useful information about the Error that we handle in the Logfile. The pieces of information are presented in very neatly fashion so we can immediately see:

  • which error occurred,
  • what were the message and exception,
  • where the error occurred (script name, script location, line number and line of the code in the script)
  • even the call stack is shown if needed.

Here are the final result and an example of one formatted error logged in Error_Log.txt file.

************************************************************************************************************
Error happend at time: 09/11/2019 18:20:41 on a computer: APP01 -  - 
Error message: The WinRM client cannot process the request. If the authentication scheme is different from Kerberos, or if the client computer is not joined to a domain, then HTTPS transport must be used or the destination machine must be added to the TrustedHosts configuration setting. Use winrm.cmd to configure TrustedHosts. Note that computers in the TrustedHosts list might not be authenticated. You can get more information about that by running the following command: winrm help config.
Error exception: Microsoft.Management.Infrastructure.CimException: The WinRM client cannot process the request. If the authentication scheme is different from Kerberos, or if the client computer is not joined to a domain, then HTTPS transport must be used or the destination machine must be added to the TrustedHosts configuration setting. Use winrm.cmd to configure TrustedHosts. Note that computers in the TrustedHosts list might not be authenticated. You can get more information about that by running the following command: winrm help config.
   at Microsoft.Management.Infrastructure.Internal.Operations.CimAsyncObserverProxyBase`1.ProcessNativeCallback(OperationCallbackProcessingContext callbackProcessingContext, T currentItem, Boolean moreResults, MiResult operationResult, String errorMessage, InstanceHandle errorDetailsHandle)
Failing script: C:UsersdekibDocumentsWindowsPowerShellModules3commonGetCPUInfo.ps1
Failing at line number: 214
Failing at line:             $CPUInfos = Get-CimInstance @params | 

Powershell command path: C:UsersdekibDocumentsWindowsPowerShellModules3commonGetCPUInfo.ps1
Position message: C:UsersdekibDocumentsWindowsPowerShellModules3commonGetCPUInfo.ps1
Stack trace: at Get-CPUInfo, C:UsersdekibDocumentsWindowsPowerShellModules3commonGetCPUInfo.ps1: line 214
at , : line 1
------------------------------------------------------------------------------------------------------------

TIP: If your scripts are scheduled in Task Manager the best practice is to have a routine of regularly checking the Error Log file and investigate the errors that occurred since the last check. I am doing this once a week.

Errors In PowerShell

There are two types of Errors in PowerShell:

  • Terminating
  • Non-Terminating

Terminating Errors

Here are the important features of Terminating errors:

  • terminates execution of command or script
  • triggers Catch block and can be error handled by the Catch block.

Examples are syntax errors, non-existent CmdLets, or other fatal errors

Non-Terminating Errors

Here are important features of Non-Terminating Error:

  • A non-fatal error.
  • Allows execution to continue despite the failure that just occurred.
  • It doesn’t trigger the Catch block and cannot be Error Handled in the Catch block by default.

Examples are permission problems, file not found, etc.

How To Force Non-Terminating Errors Into Terminating

Use the ErrorAction parameter with value Stop to force non-terminating error into terminating as in the following example. The reason why we want to make non-termination error into terminating one is to be able to catch the error when occurs.

$AuthorizedUser = Get-Content .DocumentsWindowsPowerShellProfile.ps1 -ErrorAction Stop 

Basically, the workflow is as follows.

  • When an error occurs,
  • a non-terminating error has changed into terminating one since we have Stop value on ErrrorAction parameter,
  • then since terminating error has occurred try block will send the error handling to catch block where error can be processed and
  • optionally written to the external log,
  • optionally error handling can continue in the final block.

NOTE: ErrorAction parameter overrides temporarily ErrorActionPreference variable while the call to CmdLet has been processed.

How To Treat All Errors As Terminating

We use the ErrorActionPreference variable to treat all errors as terminating by setting to the value Stop in the:

  • script
  • or session

Write the following line of code at the begging of the script to treat all errors as terminating.

$ErrorActionPreference = Stop

Type in Windows PowerShell Console the same command to setup terminating errors for the session.

ErrorAction Common Parameter

ErrorAction parameter belongs to the set of common parameters that we can use with any CmdLet. If we set CmdLetBinding on Advanced Functions than PowerShell automatically makes common parameters available for that command.

This is a parameter that I always use with CmdLets that need error handling since the ErrorAction Parameter determines how the CmdLet responds to a non-terminating error. It has several values that we will talk about in a minute but the value that I like to use is Stop and it is used to make non-terminating errors into terminating errors as written in previous sections.

The ErrorAction parameter overrides the value of the $ErrorActionPreference variable when applied to the specific command.

Here are valid values:

  • Continue (Default)
    • This is the default setting. Display error then continues execution.
  • Stop
    • Display error, and stop the execution.
  • Inquire
    • Displays error message and the user is asked to continue with execution.
  • SilentlyContinue
    • No error message is displayed and execution is continued. However, the error message is added to the $Error automatic variable.
  • Ignore
    • The same as SilentlyContinue, No error message is displayed and execution is continued. However, Ignore does not add an error message to the $Error automatic variable.
  • Suspend
    • This one is for workflows. A workflow job is suspended to investigate what happened, then the workflow can be resumed.

$ErrorActionPreference Preference Variable Explained

$ErrorActionPreference preference variable determines how Windows PowerShell responds to a non-terminating error (an error that does not stop the cmdlet processing) in a script, cmdlet or at the command line

If we want to override the value of the ErrorActionPreference preference variable for the specific command we use the ErrorAction common parameter as explained here.

The valid values for $ErrorActionPreference preference variable are:

  • Continue (Default)
    • This is the default setting. Display error then continues execution.
  • Stop
    • Display error message, and stop the execution.
  • Inquire
    • Displays error message and the user is asked to continue with execution.
  • SilentlyContinue
    • No error message is displayed and execution is continued. However, the error message is added to the $Error automatic variable.
  • Suspend
    • This one is for workflows. A workflow job is suspended to investigate what happened, then the workflow can be resumed.

Error Handling With Try/Catch/Finally Blocks

Try, Catch, and Finally, blocks are used to handle terminating errors in the scripts, functions, and CmdLets. A non-terminating error does not trigger Try block and Windows PowerShell will not look for Catch block to handle the error. So we need to force a non-terminating error to become terminating error using ErrorAction parameter with value Stop whenever we call some CmdLet or Advanced function.

Try block is used as part of the code that PowerShell will monitor for errors. The workflow in the Try block is as follows:

  • Try block is the section of code that will be monitored for errors by Windows PowerShell.
  • When the error occurs within Try block the error is saved to $Error automatic variable first.
  • Windows PowerShell searches for a Catch block to handle the error if the error is terminating. If the Catch block has not been found in current scope Windows PowerShell will search for catch block in parent scopes for nested calls.
  • Then the Finally block is run if exists.
  • If there is no Catch block than the error is not handled and the error is written to the error stream.

One Try block can have several Catch Blocks that will handle different error types.

Catch block usually handles the error.

The Finally block is optional and can be only one. Usually, it is used to clean up and free the resources.

The syntax for Try, Catch, and Finally block:

try { } 
catch [],[]
   { } 
catch { } 
finally { }

Getting Error Information With $Error Automatic Variable

$Error automatic variable is an array of error objects (both terminating and the non-terminating) that occurred in the current PowerShell session. The most recent error that occurred is written with index 0 in the array as $Error[0]. If we have just opened the Windows PowerShell session the $Error variable is an empty array and ready to be used.

Check the number of Errors in $Error variable with the following code:

$Error.Count

To prevent the error from being written in $Error automatic variable set ErrorAction parameter to value Ignore.

$Error variable is a rich object that has many useful properties worth reading and helpful for further understanding of the error that just occurred.

Let’s see some useful properties and in section Write-ErrorLog CmdLet Explained I have explained to you some useful examples of properties that are interesting to be written in an external log file.

$error[0] | Get-Member
Methods and Properties of the Error object
$Error.CategoryInfo | Get-Member
Methods and Properties of CategoryInfo object
$Error[0].Exception
The exception of the error that occurred.
$Error.InvocationInfo | Get-Member
Methods and Properties of InvocationInfo object

Write-Error CmdLet

Write-Error CmdLet writes an object to the error stream.

Please read this article from Microsoft PowerShell documentation regarding this CmdLet.

Handling Errors from non-PowerShell processes

We can run applications from PowerShell script like for example, PsExec.exe or robocopy.exe and they are external processes for PowerShell. Since it is an external process, errors from it will not be caught by our try/catch blocks in PowerShell script. So how we will know whether our external process was successful or not.

Well, we can use the $LastExitCode PowerShell automatic variable.

PowerShell will write the exit code to the $LastExitCode automatic variable when the external process exits. Check the external tool’s documentation for exit code values but usually, 0 means success and 1 or greater values mean a failure.

Useful PowerShell Error Handling Articles

Here are some useful articles and resources:

  • Windows PowerShell Error Reporting
  • About Try Catch Finally
  • About CommonParameters
  • About Automatic Variables
  • About Preference Variables
  • Write-Error
  • About Throw
  • About Break
  • About Continue

You have had it happen before. You run a PowerShell script, and suddenly the console is full of errors. Did you know you can handle these errors in a much better way? Enter PowerShell try catch blocks!

Using error handling with PowerShell try catch blocks allows for managing and responding to these terminating errors. In this post, you will be introduced to PowerShell try catch blocks and learn how to handle specific exception messages.

Understanding PowerShell Try Catch Syntax

The PowerShell try catch block syntax is straightforward. It is composed of two sections enclosed in curly brackets. The first identified section is the try block, and the second section is the catch block.

try {
    # Command(s) to try
}
catch {
    # What to do with terminating errors
}

The try block can have as many statements in it as you want; however, keep the statements to as few as possible, probably just a single statement. The point of error handling is to work with one statement at a time and deal with anything that occurs from the error.

Here is an example of an error occurring in the PowerShell console. The command is creating a new file using the New-Item cmdlet and specifying a non-existent folder for Path.

powershell error message

If this command was in a script, the output wastes some screen space, and the problem may not be immediately visible. Using a PowerShell try catch block, you can manipulate the error output and make it more readable.

Here is the same New-Item command in a try catch block. Note that line 5 uses the -ErrorAction parameter with a value of Stop to the command. Not all errors are considered “terminating,” so sometimes you need to add this bit of code to terminate into the catch block properly.

try {
    New-Item -Path C:doesnotexist `
        -Name myfile.txt `
        -ItemType File `
        -ErrorAction Stop
}
catch {
    Write-Warning -Message "Oops, ran into an issue"
}
powershell try catch block

Instead of a block of red angry-looking text, you have a simple warning message that it ran into an issue. The non-existent Path name along with forcing -ErrorAction Stop drops the logic into the catch block and displays the custom warning.

Adding the $Error Variable to Catch Output

While more readable, this is not very helpful. All you know is the command did not complete successfully, but you don’t know why. Instead of displaying my custom message, you can display the specific error message that occurred instead of the entire exception block.

When an error occurs in the try block, it is saved to the automatic variable named $Error. The $Error variable contains an array of recent errors, and you can reference the most recent error in the array at index 0.

try {
    New-Item -Path C:doesnotexist `
        -Name myfile.txt `
        -ItemType File `
        -ErrorAction Stop
}
catch {
    Write-Warning $Error[0]
}
powershell try catch block using $Error variable

The warning output is now more descriptive showing that the command failed because it couldn’t find part of the path. This message was a part of our original error message but is now more concise.

Alternatively, you can save the incoming error to a variable using $_. The dollar sign + underscore in PowerShell indicates the current item in the pipeline. In this case, the current item is the error message coming out of the try block.

Once you have saved this incoming message, you use it in a custom output message, like so:

try {
    New-Item -Path C:doesnotexist `
        -Name myfile.txt `
        -ItemType File `
        -ErrorAction Stop
}
catch {
    $message = $_
    Write-Warning "Something happened! $message"
}
try catch error messages

Adding Exception Messages to Catch Blocks

You can also use multiple catch blocks to handle specific errors differently. This example displays two different custom messages:

  • One for if the path does not exist
  • One for if an illegal character is used in the name

Note that the following screenshot shows the script running twice with two different commands in the try block. Each command, catch block, and the orange and green arrows indicate the final output.

Try with multiple catch statements

Looking at lines 14-16, there is a third catch block without an exception message. This is a “catch-all” block that will run if the error does not match any other exceptions. If you are running this script and seeing the message in the last catch block, you know the error is not related to an illegal character in the file name or part of the path not being valid.

Now how do you find the exception messages to use in the first two catch blocks? You find it by looking at the different information attached to the $Error variable. After a failed command occurs, you can run $Error[0].Exception.GetType().FullName to view the exception message for the last error that occurred.

Going back to the PowerShell console, rerun the New-Item command with a non-existent path, then run the $Error command to find the exception message.

powershell find exception message

The red text immediately following the failed command also contains the exception message but does not contain which module it is from. Looking at the $Error variable shows the full message to be used for a catch block.

Getting Exception Messages in PowerShell 7

PowerShell version 7 introduced the Get-Error command. This command displays the most recent error from the current session. After encountering an error, run Get-Error to show the exception type to use in the catch block.

powershell 7 get-error exception message type
Finding exception message type in PowerShell 7

Summary

Using PowerShell try catch blocks gives additional power to handle errors in a script and take different actions based on the error. The catch block can display more than just an error message. It can contain logic that will resolve the error and continue executing the rest of the script.

Do you have any tips on using try/catch blocks? Leave a comment below or find me on Twitter or LinkedIn for additional discussion.

Enjoyed this article? Check out more of my PowerShell articles here!

References:

Microsoft Docs: About Try Catch Finally
Microsoft Docs: About Automatic Variables ($Error)
Microsoft Blog: Understanding Non-Terminating Errors in PowerShell

Note:

  • This answer is about writing to stderr from the perspective of the outside world when a PowerShell script is called from there; while the answer is written from the perspective of the Windows shell, cmd.exe, it equally applies to Unix shells such as bash when combined with PowerShell Core.

  • By contrast, from within Powershell, you should use Write-Error, as explained in Keith Hill’s answer.

  • Sadly, there is no unified approach that will work from both within PowerShell and from the outside — see this answer of mine for a discussion.


To add to @Chris Sear’s great answer:

While $host.ui.WriteErrorLine should work in all hosts, it doesn’t (by default) write to stderr when invoked via cmd.exe, such as from a batch file.
[Console]::Error.WriteLine, by contrast, always does.

So if you want to write a PowerShell script that plays nicely in terms of output streams when invoked from cmd.exe, use the following function, Write-StdErr, which uses [Console]::Error.WriteLine
in the regular PS / cmd.exe host (console window), and $host.ui.WriteErrorLine otherwise:

<#
.SYNOPSIS
Writes text to stderr when running in a regular console window,
to the host''s error stream otherwise.

.DESCRIPTION
Writing to true stderr allows you to write a well-behaved CLI
as a PS script that can be invoked from a batch file, for instance.

Note that PS by default sends ALL its streams to *stdout* when invoked from
cmd.exe.

This function acts similarly to Write-Host in that it simply calls
.ToString() on its input; to get the default output format, invoke
it via a pipeline and precede with Out-String.

#>
function Write-StdErr {
  param ([PSObject] $InputObject)
  $outFunc = if ($Host.Name -eq 'ConsoleHost') {
    [Console]::Error.WriteLine
  } else {
    $host.ui.WriteErrorLine
  }
  if ($InputObject) {
    [void] $outFunc.Invoke($InputObject.ToString())
  } else {
    [string[]] $lines = @()
    $Input | % { $lines += $_.ToString() }
    [void] $outFunc.Invoke($lines -join "`r`n")
  }
}

Optional background information: How the PowerShell CLI’s output streams are seen by outside callers:

Internally, PowerShell has more than the traditional output streams (stdout and stderr), and their count has increased over time (try Write-Warning "I'll go unheard." 3> $null as an example, and read more at Get-Help about_Redirection.

When interfacing with the outside world, PowerShell must map the non-traditional output streams to stdout and stderr.

Strangely, however, PowerShell by default sends all its streams (including Write-Host and $host.ui.WriteErrorLine() output) to stdout when invoked from cmd.exe, even though mapping PowerShell’s error stream to stderr would be the logical choice. This behavior has been in effect since (at least) v2 and still applies as of v5.1 (and probably won’t change for reasons of backward compatibility — see GitHub issue #7989).

You can verify this with the following command, if you invoke it from cmd.exe:

powershell -noprofile -command "'out'; Write-Error 'err'; Write-Warning 'warn'; Write-Verbose -Verbose 'verbose'; $DebugPreference='Continue'; write-debug 'debug'; $InformationPreference='Continue'; Write-Information 'info'; Write-Host 'host'; $host.ui.WriteErrorLine('uierr'); [Console]::Error.WriteLine('cerr')" >NUL

The command writes to all PowerShell output streams (when you run on a pre-PowerShell-v5 version, you’ll see an additional error message relating to Write-Information, which was introduced in PowerShell v5) and has cmd.exe redirect stdout only to NUL (i.e., suppress stdout output; >NUL).

You will see no output except cerr (from [Console]::Error.WriteLine(), which writes directly to stderr) — all of PowerShell’s streams were sent to stdout.

Perhaps even more strangely, it is possible to capture PowerShell’s error stream, but only with a redirection:

If you change >NUL to 2>NUL above, it is exclusively PowerShell’s error stream and $host.ui.WriteErrorLine() output that will be suppressed; of course, as with any redirection, you can alternatively send it to a file.
(As stated, [Console]::Error.WriteLine()] always outputs to stderr, whether the latter is redirected or not.)

To give a more focused example (again, run from cmd.exe):

powershell -noprofile -command "'out'; Write-Error 'err'" 2>NUL

The above only outputs outWrite-Error‘s output is suppressed.

To summarize:

  • Without any (cmd.exe) redirection or with only a stdout redirection (>... or 1>...), PowerShell sends all its output streams to stdout.

  • With a stderr redirection (2>...), PowerShell selectively sends its error stream to stderr (irrespective of whether stdout is also redirected or not).

  • As a corollary, the following common idiom does not work as expected:

    powershell ... >data-output.txt
    This will not, as one might expect, send only stdout to file data-output.txt while printing stderr output to the terminal; instead, you’d have to use

    powershell ... >data-output.txt 2>err-output.tmp; type err-output.tmp >&2; del err-output.tmp

It follows that PowerShell is aware of cmd.exe‘s redirections and adjusts its behavior intentionally.
(This is also evident from PowerShell producing colored output in the cmd.exe console while stripping the color codes when output is redirected to a file.)

Понравилась статья? Поделить с друзьями:
  • Powershell продолжить выполнение скрипта при ошибке
  • Powershell проверка на ошибку
  • Powershell проверка диска на ошибки
  • Powershell подавление вывода ошибок
  • Powershell ошибка развертывания