PowerShell v2 CTP3 advanced function parameter '-WarningAction' not working
I'm currently in the process of writing some PowerShell scripts for managing our environment, and want to make use of the 'WarningAction' common parameter. However it doesn't seem to work :-(.
I'm using the [CmdletBinding()] attribute in my script to enable common parameters (http://msdn.microsoft.com/en-us/library/dd901844(VS.85).aspx).
Consider the following PowerShell v2 CTP3 script test.ps1:
==============[CmdletBinding()]
==============
param()
Write-Host "warning: $WarningPreference"
Write-Host "error: $ErrorActionPreference"
Write-Warning "b"
Write-Error "c"
If I execute the script as follows:
==============
PS > .\test.ps1 -ErrorAction SilentlyContinue
==============
warning: Continue
error: SilentlyContinue
WARNING: b
As expected, the $ErrorActionPreference is set to 'SilentlyContinue', and the output from the Write-Error cmd is suppressed.
However, if I execute:
==============
PS > .\test.ps1 -WarningAction SilentlyContinue
==============
warning: Continue
error: Continue
WARNING: b
D:\Misc\scripts\PowerShell\test.ps1 : c
At line:1 char:11
+ .\test.ps1 <<<< -WarningAction SilentlyContinue
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,test.ps1
The $WarningPreference isn't set, and the output from the Write-Warning cmd is not suppressed as would be expected.
Does this happen for anyone else? Or is there something obvious I'm missing?
Running Get-Help indicates that -WarningAction is supported:
==============
PS D:\> Get-Help .\test.ps1
test.ps1 [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
==============
Thanks
Michael
Note: I have already posted another version of this question in the Scripting Guys forum (see link below). It was suggested I post this here as well.
http://social.technet.microsoft.com/Forums/en/ITCG/thread/1e7ea871-432f-4650-ac1e-5a5a5e663f40
Answers
- Believe it or not, for that parameters to take effect you need to use the special methods documented in about_Functions_Advanced_Methods which are all methods of the special magic variable $PSCmdlet ...
[CmdletBinding()] param() Write-Host "warning: $WarningPreference" Write-Host "error: $ErrorActionPreference" $PSCmdlet.WriteWarning( "b" ) $PSCmdlet.WriteError( "c" )
The documentation actually says that you "can also use the various Write cmdlets," but at least in this case, it's wrong. :-(- Marked As Answer byMervyn ZhangMSFT, ModeratorFriday, November 06, 2009 5:49 AM
- Marked As Answer byMarco ShawMVP, ModeratorTuesday, November 03, 2009 12:03 PM
- Unmarked As Answer byMarco ShawMVP, ModeratorThursday, November 05, 2009 1:55 AM
- Unproposed As Answer byMarco ShawMVP, ModeratorThursday, November 05, 2009 1:55 AM
- Proposed As Answer byJoel -Jaykul- BennettMVPTuesday, November 03, 2009 6:28 AM
- Ok, now, this is mostly a joke, but .. if you REALLY need those warnings to behave, it has to look something like this:
function TestWarningA {[CmdletBinding()]param()
# This has no effect on the WarningVariable, but without it the warnings are displayed in the host
if($PSBoundParameters.ContainsKey("WarningAction")){ $local:WarningPreference = $PSBoundParameters['WarningAction'] }
Write-Host "Script A: $WarningPreference"
$PSCmdlet.WriteWarning("WA1") # this is the only thing affected by -WarningAction
if($PSBoundParameters.ContainsKey("WarningVariable")){
Write-Warning "WA2" -WarningVariable $("+$($PSBoundParameters['WarningVariable'].TrimStart('+'))")
} else {
Write-Warning "WA2"
}
$PSCmdlet.WriteError((new-object System.Management.Automation.ErrorRecord( (new-object System.Exception "EA1"), "TestError", "NotSpecified", $null)))
Write-Error "EA2"
if($PSBoundParameters.ContainsKey("WarningVariable")){
TestWarningB -WarningVariable $("+$($PSBoundParameters['WarningVariable'].TrimStart('+'))")
} else {
TestWarningB
}
}
function TestWarningB {[CmdletBinding()]param()
Write-Host "Script B: $WarningPreference $($PSBoundParameters['WarningVariable'])"
$PSCmdlet.WriteWarning("WB1")
if($PSBoundParameters.ContainsKey("WarningVariable")){
Write-Warning "WB2" -WarningVariable $("+$($PSBoundParameters['WarningVariable'].TrimStart('+'))")
} else {
Write-Warning "WB2"
}
$PSCmdlet.WriteError( (new-object System.Management.Automation.ErrorRecord( (new-object System.Exception "EB1"), "TestError", "NotSpecified", $null)) )
Write-Error "EB2"
}
TestWarningA -WarningAction:SilentlyContinue -WarningVariable wv -ErrorAction:SilentlyContinue -ErrorVariable ev
- Marked As Answer byMervyn ZhangMSFT, ModeratorFriday, November 06, 2009 5:48 AM
All Replies
- At this point, v2 has been offiicially released for all platforms. That being said, I did try this on PowerShell v2 on Server 2008 R2 and got the same error. I'll ask around and make sure to test on a RTM release...
- Believe it or not, for that parameters to take effect you need to use the special methods documented in about_Functions_Advanced_Methods which are all methods of the special magic variable $PSCmdlet ...
[CmdletBinding()] param() Write-Host "warning: $WarningPreference" Write-Host "error: $ErrorActionPreference" $PSCmdlet.WriteWarning( "b" ) $PSCmdlet.WriteError( "c" )
The documentation actually says that you "can also use the various Write cmdlets," but at least in this case, it's wrong. :-(- Marked As Answer byMervyn ZhangMSFT, ModeratorFriday, November 06, 2009 5:49 AM
- Marked As Answer byMarco ShawMVP, ModeratorTuesday, November 03, 2009 12:03 PM
- Unmarked As Answer byMarco ShawMVP, ModeratorThursday, November 05, 2009 1:55 AM
- Unproposed As Answer byMarco ShawMVP, ModeratorThursday, November 05, 2009 1:55 AM
- Proposed As Answer byJoel -Jaykul- BennettMVPTuesday, November 03, 2009 6:28 AM
- Ahhh... thanks Joel, that does work! So is that a bug or what? Write-Error and the others like Write-Verbose and Write-Debug seem to work fine, it's just Write-Warning that isn't.
So using the $PSCmdlet.WriteWarning() format I was able suppress the error and capture it using -WarningVariable. Mission accomplished!
Michael
Oh btw I also tried this in Windows 7, same problem. So looks like this is a problem in RTM. Ok... after playing around a bit more with this, I've stumbled across my next problem. Using $PSCmdlet.WriteWarning(), how can I pass the warnings back through multiple levels of calling scripts?
For example, consider the following two scripts:
====
# TestWarningA.ps1
[CmdletBinding()]
param()Write-Host "Script A"
$PSCmdlet.WriteWarning("A").\TestWarningB.ps1
====
and
====
# TestWarningB.ps1
[CmdletBinding()]
param()Write-Host "Script B"
$PSCmdlet.WriteWarning("B")
====
Executing TestWarningA.ps1 with -WarningAction SilentlyContinue returns:
====
PS D:\> .\TestWarningA.ps1 -WarningAction SilentlyContinue
Script A
Script B
WARNING: B
====
The warning in TestWarningB.ps1 is not suppressed as I had intended and only the first warning is returned to the -WarningVariable. The same scenario but using Write-Error does work as expected. Is there a way to work around this using $PSCmdlet.WriteWarning()?
I need to be able to capture all the warnings from the sub scripts to action appropriately in the master script (e.g. log to event log).
Let me know if I should log this as a new forum entry - will do so if I get no response due to this thread already being marked 'answered'.
Thanks
Michael- I've unmarked Jaykul's response as the answer, but may go back on that later.
- Ugh. Yeah, it was definitely a bug already, in my opinion, this makes it doubly so.
Marco: aww, you could at least vote me helpful :-P
The problem is that the WarningAction common parameter doesn't actually set/override the value of the $WarningPreference variable the way it's supposed to (try printing it out, you'll see), and therefore the setting isn't propagated to nested commands.
If you put a line: { Get-Variable -Scope Local } in your TestWarningA, you'll see that passing -ErrorAction:SilentlyContinue actually results in the variable being defined for that local scope, but the same is NOT true for the -WarningAction:SilentlyContinue parameter (which it seems should behave the same way).
You could make it work for any given function by adding this at the top:
if($PSBoundParameters.ContainsKey("WarningAction")){
$local:WarningPreference = $PSBoundParameters['WarningAction']
}
I'll send a note to the MVP mailing list, but I filed a bug too: https://connect.microsoft.com/PowerShell/feedback/ViewFeedback.aspx?FeedbackID=508387- Edited byJoel -Jaykul- BennettMVPThursday, November 05, 2009 6:06 AMlinkifying
- Edited byJoel -Jaykul- BennettMVPThursday, November 05, 2009 6:32 AM
- Ok, now, this is mostly a joke, but .. if you REALLY need those warnings to behave, it has to look something like this:
function TestWarningA {[CmdletBinding()]param()
# This has no effect on the WarningVariable, but without it the warnings are displayed in the host
if($PSBoundParameters.ContainsKey("WarningAction")){ $local:WarningPreference = $PSBoundParameters['WarningAction'] }
Write-Host "Script A: $WarningPreference"
$PSCmdlet.WriteWarning("WA1") # this is the only thing affected by -WarningAction
if($PSBoundParameters.ContainsKey("WarningVariable")){
Write-Warning "WA2" -WarningVariable $("+$($PSBoundParameters['WarningVariable'].TrimStart('+'))")
} else {
Write-Warning "WA2"
}
$PSCmdlet.WriteError((new-object System.Management.Automation.ErrorRecord( (new-object System.Exception "EA1"), "TestError", "NotSpecified", $null)))
Write-Error "EA2"
if($PSBoundParameters.ContainsKey("WarningVariable")){
TestWarningB -WarningVariable $("+$($PSBoundParameters['WarningVariable'].TrimStart('+'))")
} else {
TestWarningB
}
}
function TestWarningB {[CmdletBinding()]param()
Write-Host "Script B: $WarningPreference $($PSBoundParameters['WarningVariable'])"
$PSCmdlet.WriteWarning("WB1")
if($PSBoundParameters.ContainsKey("WarningVariable")){
Write-Warning "WB2" -WarningVariable $("+$($PSBoundParameters['WarningVariable'].TrimStart('+'))")
} else {
Write-Warning "WB2"
}
$PSCmdlet.WriteError( (new-object System.Management.Automation.ErrorRecord( (new-object System.Exception "EB1"), "TestError", "NotSpecified", $null)) )
Write-Error "EB2"
}
TestWarningA -WarningAction:SilentlyContinue -WarningVariable wv -ErrorAction:SilentlyContinue -ErrorVariable ev
- Marked As Answer byMervyn ZhangMSFT, ModeratorFriday, November 06, 2009 5:48 AM
- Wow you guys rock!
Joel - thanks for the code example, no way I could have figured that out myself. Looking at the code I'm not sure I'm that keen on getting it working :-). I didn't mention before, but I actually have three levels of scripts so far calling each other so would have to do this at each level... yikes.
I'm happy knowing it's a bug that will (hopefully!) be fixed sometime... but I won't hold my breath. In the meantime I'll use Write-Error or Write-Output instead (which won't work exactly as I imagined it happening, but will have to make do!).
So Joel answered both my questions, does that mean he gets twice the credit?
Michael

