I've been giving PowerShell (v3.0) a shot for the first time today, and became enormously frustrated with the strange way some of its error-handling concepts are implemented.
I wrote the following piece of code (using the Remote Registry PowerShell Module)
try
{
New-RegKey -ComputerName $PCName -Key $Key -Name $Value
Write-Host -fore Green ($Key + ": created")
}
catch
{
Write-Host -fore Red "Unable to create RegKey: " $Key
Write-Host -fore Red $_
}
(This is just a snippet)
Apparently the default behavior of PowerShell is to NOT catch errors which are non-terminating. So I added the following line at the top op my script, as recommended by various people:
$ErrorActionPreference = "Stop"
Executing this in the PowerShell ISE did indeed catch all errors. However, execution following command from the terminal still does not catch my errors.
From ISE:
PS C:\windows\system32> C:\Data\Scripts\PowerShell\Error.ps1
Errorhandling: Stop
SOFTWARE\MySoftware does not exist. Attempting to create
Unable to create RegKey: SOFTWARE\MySoftware
Key 'SOFTWARE\MySoftware' doesn't exist.
From Command-Line:
PS C:\Data\Scripts\PowerShell> .\Error.ps1
Errorhandling: Stop
SOFTWARE\MySoftware does not exist. Attempting to create
New-RegKey : Key 'SOFTWARE\MySoftware' doesn't exist.
At C:\Data\Scripts\PowerShell\Error.ps1:17 char:13
+ New-RegKey -ComputerName $PCName -Key $Key -Name $Value
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,New-RegKey
SOFTWARE\MySoftware: created
I have no idea why the behavior of the Preference Variables behaves differently depending on where they are called from, especially since the ISE seems to execute the exact same command?
Based on other feedback, I changed the following line:
New-RegKey -ComputerName $PCName -Key $Key -Name $Value
To:
New-RegKey -ComputerName $PCName -Key $Key -Name $Value -ErrorAction Stop
Using this method, I was able to trap errors from both the command-line and the ISE, but I don't want to specify the error behaviour on each Cmdlet I call, especially because the catching of errors is essential to the proper functioning of the code. (Plus, the fact that this method DOES work only serves to confuse me even more)
What is the proper way of defining error-handling behavior for the scope of an entire script and/or module?
Also, here's my $PSVersionTable:
PS C:\Data\Scripts\PowerShell> $PSVersionTable
Name Value
---- -----
PSVersion 3.0
WSManStackVersion 3.0
SerializationVersion 1.1.0.1
CLRVersion 4.0.30319.18408
BuildVersion 6.2.9200.16481
PSCompatibleVersions {1.0, 2.0, 3.0}
PSRemotingProtocolVersion 2.2
Since you're running V3, you also have the option of using $PSDefaultParameterValues:
$PSDefaultParameterValues += @{'New-RegKey:ErrorAction' = 'Stop'}
Normally that will change it in the global scope. If you want to isolate it to just the local or script scope, you can initialize a new one in the local scope first:
$PSDefaultParameterValues = @{}
$PSDefaultParameterValues += @{'New-RegKey:ErrorAction' = 'Stop'}
If you want to inherit what's already in the parent scope and then add to it for the local scope:
$PSDefaultParameterValues = $PSDefaultParameterValues.clone()
$PSDefaultParameterValues += @{'New-RegKey:ErrorAction' = 'Stop'}
To set the default ErrorAction for all cmdlets, not just New-RegKey, specify '*:ErrorAction'
instead of 'New-RegKey:ErrorAction'
in the code above.