How to ignore warning errors? - powershell

I have the following PowerShell script. It picks up the NetBIOS name of computers within a given IP address. I'm using a pipe so as to dump the results into a text file. The problem is that if an IP address is not available, a warning is printed.
This is the PowerShell script:
function Get-ComputerNameByIP {
param( $IPAddress = $null )
BEGIN {
$prefBackup = $WarningPreference
$WarningPreference = 'SilentlyContinue'
}
PROCESS {
if ($IPAddress -and $_) {
throw ‘Please use either pipeline or input parameter’
break
} elseif ($IPAddress) {
([System.Net.Dns]::GetHostbyAddress($IPAddress))
}
} else {
$IPAddress = Read-Host “Please supply the IP Address”
[System.Net.Dns]::GetHostbyAddress($IPAddress)
}
}
END {
$WarningPreference = $prefBackup
}
This is the error message I wish to ignore:
WARNING: The requested name is valid, but no data of the requested type was found

You can use common parameter -WarningAction:SilentlyContinue with the command that generates warning. It's better than separately overriding $WarningPreference before executing the command and reverting it back afterwards as was suggested above - this parameter basically does that for you.
The WarningAction parameter overrides the value of the $WarningPreference variable for the current command. Because the default value of the $WarningPreference variable is Continue, warnings are displayed and execution continues unless you use the WarningAction parameter.
See more here.

You want to suppress warnings, not errors. Warnings can be silenced completely by setting the $WarningPreference variable to SilentlyContinue:
PS C:\> Write-Warning 'foo'
WARNING: foo
PS C:\> $prefBackup = $WarningPreference
PS C:\> $WarningPreference = 'SilentlyContinue'
PS C:\> Write-Warning 'foo'
PS C:\> $WarningPreference = $prefBackup
PS C:\> Write-Warning 'foo'
WARNING: foo
The setting pertains to the current scope, so if you want to suppress all warnings for your function you'd simply set the preference at the beginning of your function:
function Get-ComputerNameByIP {
param( $IPAddress = $null )
BEGIN {
$WarningPreference = 'SilentlyContinue'
}
PROCESS {
if ($IPAddress -and $_) {
throw ‘Please use either pipeline or input parameter’
break
} elseif ($IPAddress) {
[System.Net.Dns]::GetHostbyAddress($IPAddress)
}
[System.Net.Dns]::GetHostbyAddress($_)
} else {
$IPAddress = Read-Host "Please supply the IP Address"
[System.Net.Dns]::GetHostbyAddress($IPAddress)
}
}
END {}
}
If you want warnings suppressed for specific statements only, a simpler way is to redirect the warning output stream to $null:
[System.Net.Dns]::GetHostbyAddress($IPAddress) 3>$null
Warning stream redirection is only available in PowerShell v3 and newer, though.

$ErrorActionPreference = 'SilentlyContinue'
This global var controls error output of those commands that provide intermittent (non-terminating) errors and warnings. Your error is of this kind, so set preference to silently continue to suppress these warnings.

You could use a try/catch block for something like this. Consider the following example using a proper formed IP address but had no associated record.
try{
[System.Net.Dns]::GetHostbyAddress("127.0.0.56")
} Catch [System.Management.Automation.MethodInvocationException]{
Write-Host "Nothing Record Found"
}
When I tested this the error you were seeing was being caught as [System.Management.Automation.MethodInvocationException] so I checked for that specific error type. Based on it's name I'm sure there are other reasons for it to be called. It is possible to just omit that part altogether and it will catch all errors. Since you account for some other possibilities maybe you don't need it.
If that was a concern you could check the text of the $_.Exception.InnerException to see if it matches the error as well. In the above case it contains the text "The requested name is valid, but no data of the requested type was found".
This might be wrong because I am curious as to why your error is prefixed with "WARNING" where mine is not. A little more research on both our parts might be needed.

You can trap the error and force PowerShell to do nothing with it, kind of like a Try/Catch but global for the whole script:
TRAP {"" ;continue}
[System.Net.Dns]::GetHostbyAddress($IPAddress)

Related

Function to Get Value from Registry - 2 issues: DWORD, console error

This is ma very simple PowerShell function to get reg value from registry:
Function GetRegKey {
Param([String]$GetRegKeyPath, $GetRegKeyName)
If (Test-Path -Path $GetRegKeyPath) {
If (Get-ItemProperty -Path $GetRegKeyPath -Name $GetRegKeyName) {
$GetRegKeyVal = (Get-ItemPropertyValue $GetRegKeyPath $GetRegKeyName)
Return $GetRegKeyVal
} Else {
write-output ("Warning! Path: '$GetRegKeyPath' is exist, but RegKey: '$GetRegKeyName' is: not exist.") }
} Else {
write-output ("Warning! Path: '$GetRegKeyPath' is not exist!")
Return $null }
}
Example:
GetRegKey "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion" "ProgramFilesDir"
will give output like:
C:\Program Files
I have two things what is not working:
If $GetRegKeyName is not exist script besides go to command write-output ("Warning! Path: '$GetRegKeyPath' is exist, but RegKey: '$GetRegKeyName' is: not exist.") is also display in console error:
Is this can be somehow avoid? I did try with pipe | Out-Null but its bassically not a solution I guess.
Second thing what is not works here is reg with DWORD value.
Is command Get-ItemProperty -Path $GetRegKeyPath -Name $GetRegKeyName
can't be used with DWORD?
I suggest streamlining your function as follows, which only requires a single Get-ItemPropertyValue (note that I've renamed the function to GetRegValue, because it is a registry value's (data) that is returned):
Function GetRegValue {
Param(
[Parameter(Mandatory)]
[String] $KeyPath,
[Parameter(Mandatory)]
[String] $ValueName
)
try {
Get-ItemPropertyValue -ErrorAction Stop $KeyPath $ValueName
}
catch [System.Management.Automation.ItemNotFoundException] {
Write-Warning "Path: '$KeyPath' does not exist."
}
catch [System.Management.Automation.PSArgumentException] {
Write-Warning "Path: '$KeyPath' exists, but RegKey: '$ValueName' doesn't."
}
}
A try / catch statement is used to match errors by their exception type, from which the specific error condition can be inferred.
Note that any exception type not matched would result in a script-terminating error; that is, error conditions other than the matched ones cause a fatal error (by default), such as a lack of permissions.
The common -ErrorAction parameter is used with Stop in order to promote non-terminating errors to (script-)terminating ones, which allows them to be caught with try / catch.
As an aside: Due to a longstanding bug still present as of PowerShell 7.3.1, Get-ItemPropertyValue inappropriately reports a (statement-)terminating error when the key path exists, but not the value name - see GitHub issue #5906.
Write-Warning is used to emit the warnings, which uses the dedicated warning output stream, which is conceptually preferable and also gives you automatic coloring of the message.
As for DWORD values:
PowerShell is capable of returning types of all data stored in the registry (they get mapped onto equivalent .NET types), so there shouldn't be a problem with REG_DWORD registry values.
You can verify by calling the function above as follows:
GetRegValue HKCU:\Console FontSize
This should output an [int] (System.Int32) value such as 1048576.
Note that if the value is too large to fit into [int], you'll get a [uint32] (System.UInt32) value instead.

Non-terminating Error Handling - Difference between if $Error.count and $ErrorActionPreference = 'stop'

I need to handle a non-terminating error in a powershell script. What is the most efficient way to this?
To set $ErrorActionPreference variable to stop and use try/catch
$ErrorActionPreference = 'stop'
try{
functionThatCanFail
}catch{
#Do Stuff
}
Or to clear $Error variable and then evaluate if it is populated
$Error.Clear()
functionThatCanFail
if( $Error.Count -ne 0){
#Do Stuff
}
I would add [CmdletBinding()]to your function and put it inside a try/catch.
Because of the CmdletBinding you are now able to call your function with the parameter -ErrorAction Stop.
The other suggestion with the $ErrorActionor $Error.Clear()would also work but is not a 'clean' way.
function functionThatCanFail
{
[CmdletBinding()]
param
(
$Param1
)
#Do stuff
}
try
{
functionThatCanFail -ErrorAction Stop
}
catch
{
#Error case
}
The simplest approach is to use the -ErrorVariable / -ev common parameter, which records all non-terminating errors reported by a given cmdlet in a designated variable.
Note, however, that this only works with cmdlets and advanced functions / scripts, because only they support common parameters - see Patrick's helpful answer for how to define your own function as an advanced function.
# Provoke a non-terminating error and silence it,
# but store it in custom variable $err via common parameter -ErrorVariable
Get-Item /NoSuchFile -ErrorAction SilentlyContinue -ErrorVariable err
if ($err) { # if a / a least one non-terminating error was reported, handle it.
"The following non-terminating error(s) occurred:`n$err"
}
That way, the error analysis is command-scoped, without having to record the state of or alter the state of the session-level $Error collection.
Note, however, that you cannot handle terminating errors this way.
For a comprehensive overview of PowerShell error handling, see here.

Powershell redirect std error to variable

I would like to invoke an arbitrary expression and redirect std error to a variable without redirection to a file.
For example, in Powershell it is possible to redirect standard error using 2> operator. Using a temporary file, I can easily get what I want:
#$expr = "1/0" # with std error
#$expr = "2+2" # without stderror
#$expr = "Get-Service invalidsvc" # with stderror
$expr = "try { throw '111' } catch { }" # without std error
$ans = Invoke-Expression $expr -ErrorVariable ev 2> C:\log\stderr.txt
if(cat C:\log\stderr){
Write-Host "Error $ev"
}
How can I do the same, but without creation of a temporal output file?
Wrong solutions:
Using -ErrorVariable switch. Counter example:
Invoke-Expression $expr -ErrorVariable ev 2> C:\aaa\file1.txt
$expr = "try { throw '111' } catch { }"
Write-Host "Error $ev" # will output error, but std error is empty
$LASTEXITCODE and $? check. Counter example:
Get-Service invalidservice
$lastexitcode is equal to 0, $? is equal to True, but std error is not empty
The idea is simple: save the "red" (std error) text in Powershell console in a variable. The command I receive is an arbitrary string.
Examples:
When I write "try { throw '111' } catch { }" in Powershell console there will be no red (error) text in PS console (despite the fact $error is not empty). So if I invoke that expression in my code I get no error saved in some variable.
When I write "Get-Service notexistingservice", or "1/0", or "Write-Error 111" there will red (error) text and non-null $error. So if I invoke that expression in my code I would like to get error saved in some variable.
Save standard output and standard error to separate variables. It won't work without the dollar sign (from Windows Powershell in Action).
$err = $( $output = get-childitem foo3 ) 2>&1
The way to do it is the -errorvariable common parameter. Your counter example is only valid (and I only hesitantly use that word) because you have explicitly coded for it to not output an error with the use of the Try/Catch and not including anything coding in your catch. You are basically complaining that you told PowerShell to send error cases to the Catch scriptblock, where you did not output anything, and then having an issue when nothing is output. An error still occurs, it is logged in the errorvariable as you stated it should be, and also stored in $Error, but since you did not output anything in your Catch block there's nothing for your StdErr redirect to do.
If you want $ev to not contain an error because you corrected the issue in your Catch block, then also clear the variable in the catch block.
$expr = 'try{ throw "1111"}catch{Remove-Variable ev}'
Or if you want StdErr to contain the error text, make sure you include that output in your Catch block:
$expr = 'try{ throw "1111"}catch{Write-Error $_.Exception.Message}'
I know this is a very old question but I had the same problem and wanted to share what I ended up with.
$error.clear()
$List = New-Object PSObject
Invoke-Command -ComputerName $server -ScriptBlock {
$smbv1 = (Get-SmbServerConfiguration | Select EnableSMB1Protocol)
$smbv1 | Select-Object EnableSMB1Protocol} -OutVariable List
foreach($item in $list.EnableSMB1Protocol){
if($error -ne $null){
$item = "unknown"
$msg = $error.Exception.Message
ac .\smb1errors.txt "$Server, $msg"
}
Since the $error variable is a .NET object in order to clear it I needed to pass it parameter (). I then executed my code, in this case checking for SMBv1 service, and testing if $error is still $null. If the $error variable has content I grabed it in a variable. $msg = $error.Exception.Message

Alternative to Throwing Param Exceptions in PowerShell?

Bottom Line Up Front
I'm looking for a method to validate powershell (v1) command line parameters without propagating exceptions back to the command line.
Details
I have a powershell script that currently uses param in conjunction with [ValidateNotNullOrEmpty] to validate command line paramaters:
param(
[string]
[ValidateNotNullOrEmpty()]$domain = $(throw "Domain (-d) param required.")
)
We're changing the paradigm of error handling where we no longer want to pass exceptions back to the command line, but rather provide custom error messages. Since the param block can not be wrapped in a try catch block, i've resorted to something like the following:
param(
[string]$domain = $("")
)
Try{
if($domain -like $("")){
throw "Domain (-d) param required."
}
...
}Catch{
#output error message
}
My concern is that we're bypassing all of the built-in validation that is available with using param. Is my new technique a reasonable solution? Is there a better way to validate command line params while encapsulating exceptions within the script? I'm very much interested in see how PowerShell professionals would handle this situation.
Any advice would be appreciated.
You can write a custom validation script. Give this parameter a try.
Param(
[ValidateScript({
If ($_ -eq $Null -or $_ -eq "") {
Throw "Domain (-d) param required."
}
Else {
$True
}
})][string]$Domain
)
As I mentioned in a comment: more I read your description, more I come to the conclusion that you should not worry about "bypassing all built-in validation". Why? Because that's exactly your target. You want to bypass it's default behavior, so if that's what you need and have to do - than just do it. ;)
One way is to use default parameters like this [from msdn] -
Function CheckIfKeyExists
{
Param(
[Parameter(Mandatory=$false,ValueFromPipeline=$true)]
[String]
$Key = 'HKLM:\Software\DoesNotExist'
)
Process
{
Try
{
Get-ItemProperty -Path $Key -EA 'Stop'
}
Catch
{
write-warning "Error accessing $Key $($_.Exception.Message)"
}
}
}
So, here, if you try calling the function without passing any parameters, you will get warning what you have defined in your try/catch block. And, you are not using any default validation attributes for that. You should always assume that you will encounter an error, and write code that can survive the error. But the lesson here is if you implement a default value, remember that it is not being validated.
Read more here

Redirecting output to $null in PowerShell, but ensuring the variable remains set

I have some code:
$foo = someFunction
This outputs a warning message which I want to redirect to $null:
$foo = someFunction > $null
The problem is that when I do this, while successfully supressing the warning message, it also has the negative side-effect of NOT populating $foo with the result of the function.
How do I redirect the warning to $null, but still keep $foo populated?
Also, how do you redirect both standard output and standard error to null? (In Linux, it's 2>&1.)
I'd prefer this way to redirect standard output (native PowerShell)...
($foo = someFunction) | out-null
But this works too:
($foo = someFunction) > $null
To redirect just standard error after defining $foo with result of "someFunction", do
($foo = someFunction) 2> $null
This is effectively the same as mentioned above.
Or to redirect any standard error messages from "someFunction" and then defining $foo with the result:
$foo = (someFunction 2> $null)
To redirect both you have a few options:
2>&1>$null
2>&1 | out-null
ADDENDUM:
Please note that (Windows) powershell has many more streams than a linux based OS. Here's the list from MS docs:
Thus you can redirect all streams using the wildcard with *>$null, and you can also use a file instead of $null.
This should work.
$foo = someFunction 2>$null
If it's errors you want to hide you can do it like this
$ErrorActionPreference = "SilentlyContinue"; #This will hide errors
$someObject.SomeFunction();
$ErrorActionPreference = "Continue"; #Turning errors back on
Warning messages should be written using the Write-Warning cmdlet, which allows the warning messages to be suppressed with the -WarningAction parameter or the $WarningPreference automatic variable. A function needs to use CmdletBinding to implement this feature.
function WarningTest {
[CmdletBinding()]
param($n)
Write-Warning "This is a warning message for: $n."
"Parameter n = $n"
}
$a = WarningTest 'test one' -WarningAction SilentlyContinue
# To turn off warnings for multiple commads,
# use the WarningPreference variable
$WarningPreference = 'SilentlyContinue'
$b = WarningTest 'test two'
$c = WarningTest 'test three'
# Turn messages back on.
$WarningPreference = 'Continue'
$c = WarningTest 'test four'
To make it shorter at the command prompt, you can use -wa 0:
PS> WarningTest 'parameter alias test' -wa 0
Write-Error, Write-Verbose and Write-Debug offer similar functionality for their corresponding types of messages.
using a function:
function run_command ($command)
{
invoke-expression "$command *>$null"
return $_
}
if (!(run_command "dir *.txt"))
{
if (!(run_command "dir *.doc"))
{
run_command "dir *.*"
}
}
or if you like one-liners:
function run_command ($command) { invoke-expression "$command "|out-null; return $_ }
if (!(run_command "dir *.txt")) { if (!(run_command "dir *.doc")) { run_command "dir *.*" } }
Recently, I had to shut up powershell on a Linux host, this wasn't that obvious to figure out. After back and forth I found out that wrapping a command in $( ) and adding a explicit redirection after the wrapper works.
Anything else I tried, wouldn't - I still don't know why since the PowerShell Docs are of desirable quality (and full of inconsistency...)
To import all modules on startup, I added the following. This produced some stderr output by powershell that couldnt be put to rest by ErrorAction or redirection without using the wrapping...
If anyone could elaborate on why's that would be very appreciated.
# import installed modules on launch
$PsMods = $(Get-InstalledModule);
$($PsMods.forEach({ Import-Module -Name $_.Name -ErrorAction Ignore })) *> /dev/null