Powershell suppress error output

PowerShell errors are easy to suppress — but should you? There is an old saying that if you ignore something long enough, it will eventually go away. Admittedly, this saying isn’t always the best advice in day-to-day life, but it does have its place when it comes to PowerShell. As someone who writes lots […]

Содержание

  1. PowerShell errors are easy to suppress — but should you?
  2. Before I begin
  3. Error action
  4. PowerShell errors and the excessive use of error action
  5. PowerShell errors: Let them be
  6. Suppressing PowerShell Errors
  7. Introduction to Error Action in PowerShell
  8. Use the -ErrorAction Parameter in PowerShell
  9. Setting Error Action Preferences in PowerShell
  10. Discarding Output
  11. Understanding Streams
  12. Suppressing Output Stream
  13. Showing Or Hiding Streams
  14. Redirecting Streams
  15. Special Case: Write-Host
  16. Why Some Commands Are Ugly
  17. Writing Directly To Host
  18. Silencing All Output

PowerShell errors are easy to suppress — but should you?

There is an old saying that if you ignore something long enough, it will eventually go away. Admittedly, this saying isn’t always the best advice in day-to-day life, but it does have its place when it comes to PowerShell. As someone who writes lots of PowerShell scripts, I have run into plenty of situations in which a script is completely derailed by an unexpected error. In some of these cases, it’s good that the error caused the script to stop — otherwise, the script would have caused damage. In other cases, however, it would have been perfectly acceptable for the script to ignore the error and go on.

Whether you want to ignore error messages or halt a script’s execution when an error occurs, PowerShell has plenty of options for dealing with errors. In this article, I want to show you some of the techniques that I like to use.

Freepik / Designed by Gstudioimagen

Before I begin

Before I get started, I want to quickly point out that even though it is really easy to suppress PowerShell errors, doing so isn’t always the best option (although it can be). If you recklessly tell PowerShell to ignore errors, it can cause your script to behave unpredictably. Suppressing error messages also makes troubleshooting a lot more difficult.

Error action

Probably the most common technique for dealing with errors is to append the ErrorAction switch to a cmdlet. The ErrorAction switch lets you tell PowerShell what to do if the cmdlet produces an error.

If your goal is to suppress an error message and make it seem as though nothing has happened, then the error action of choice is SilentlyContinue. You can see an example of how this error action works in the image below. Here I am using the Get-Process cmdlet followed by the letter X. Since there is no process named X, the command produces an error. That error goes away when I append the SilentlyContinue error action. The error still occurs, but PowerShell simply ignores it.


PowerShell errors and the excessive use of error action

A while back, I saw a script that someone had written in which they appended -ErrorAction SilentlyContinue to nearly every cmdlet. In that particular situation, the script’s author was new to PowerShell and was desperately trying to make the errors in a script go away.

It goes without saying that ErrorAction was never intended as a magical cure-all for buggy scripts. Even so, I can envision situations in which a script has been well written, but if an error does happen to occur, then that error needs to be hidden from the end-user. In a situation like this, however, there is a more efficient option than simply appending the ErrorAction switch to every cmdlet.

If you need a script to behave in a certain way (such as suppressing any errors that occur), then you might consider setting up some preference variables. Preference variables function as configuration settings for PowerShell. For example, you might use a preference variable to control the number of history items that PowerShell retains or to force PowerShell to ask “are you sure” before performing certain actions. Here is how you can use a preference variable to set the ErrorAction to SilentlyContinue for the entire session:

You can see an example of how this works in the image below. Notice that the last command shown in the image does not include an error action, but the error is suppressed any way because of the error action preference variable that has been declared.


Incidentally, there are plenty of other error actions that you can specify other than SilentlyContinue. I won’t go into all of them, but here are some of the more useful ones:

  • Continue: PowerShell displays the error message, but the script continues to run.
  • Stop: PowerShell displays the error message and stops running the script.
  • Inquire: PowerShell displays the error message but asks if you want to continue.

There is one other reason you might want to use an error action preference variable instead of relying on the ErrorAction switch. As previously mentioned, the ErrorAction switch has to be used in conjunction with a PowerShell cmdlet. Earlier, for instance, I used the Get-Process cmdlet to demonstrate how the ErrorAction switch works.

The problem with this is that not everything that you do in PowerShell involves using a cmdlet. Simple mathematical operations can be performed without the aid of a cmdlet. Similarly, you can assign a variable without using a cmdlet. This means that you cannot append the ErrorAction switch to these types of operations. Even though you can’t append the ErrorAction switch, you can use an error action preference variable to suppress errors produced when no cmdlets are being used. Let me show you an example.


In the first line of the screen capture above, I entered 2/0, which tells PowerShell to divide two by zero. Since dividing by zero is a mathematical impossibility, PowerShell displays an error.

Next, I tried dividing by zero again, but this time I attached the ErrorAction switch. However, because the ErrorAction switch has to be appended to a cmdlet and no cmdlet is being used, the command produced an error saying that ErrorAction is not allowed.

The third command that I entered sets the error action preference variable to SilentlyContinue, and the fourth line attempts to divide two by zero once again. This time the error is suppressed. In other words, the error action preference variable can suppress errors in situations where the ErrorAction switch is not.

PowerShell errors: Let them be

It’s usually best to allow PowerShell to go ahead and display errors that might occur. After all, it’s important to know when something isn’t working right. If you need to suppress an error, you can use the ErrorAction switch to suppress an error for a single cmdlet or an ErrorAction preference variable to suppress errors globally.

Источник

Suppressing PowerShell Errors

Whether we want to ignore error messages or terminate a script’s execution when an error occurs, Windows PowerShell has plenty of options for dealing with errors. This article will discuss multiple techniques in handling and suppressing errors.

Introduction to Error Action in PowerShell

Even though it is effortless to suppress Windows PowerShell errors, doing so isn’t always the best option (although it can be). If we carelessly tell PowerShell to hide errors, it can cause our script to behave unpredictably.

Suppressing error messages also makes troubleshooting and information gathering a lot more complicated. So tread lightly and be careful about using the following snippets that you will see in this article.

Use the -ErrorAction Parameter in PowerShell

The most common method for dealing with errors is to append the -ErrorAction parameter switch to a cmdlet. The -ErrorAction parameter switch lets PowerShell tell what to do if the cmdlet produces an error.

In the command above, we are querying for a service that doesn’t exist. Usually, PowerShell will throw an error if the service doesn’t exist.

Since we use the -ErrorAction parameter, the script will continue as expected, like it doesn’t have an error.

Setting Error Action Preferences in PowerShell

If we need a script to behave in a certain way (such as suppressing errors), we might consider setting up some preference variables. Preference variables act as configuration settings for PowerShell.

We might use a preference variable to control the number of history items that PowerShell retains or force PowerShell to ask the user before performing specific actions.

For example, here is how you can use a preference variable to set the -ErrorAction parameter to SilentlyContinue for the entire session.

There are many other error actions that we can specify for the ErrorAction switch parameter.

  • Continue : PowerShell will display the error message, but the script will continue to run.
  • Ignore : PowerShell does not produce any error message, writes any error output on the host, and continues execution.
  • Stop : PowerShell will display the error message and stop running the script.
  • Inquire : PowerShell displays the error message but will ask for confirmation first if the user wants to continue.
  • SilentlyContinue : PowerShell silently continues with code execution if the code does not work or has non-terminating errors.
  • Suspend : PowerShell suspends the workflow of the script.

As previously mentioned, the -ErrorAction switch has to be used in conjunction with a PowerShell cmdlet. For instance, we used the Get-Process cmdlet to demonstrate how the ErrorAction switch works.

Marion specializes in anything Microsoft-related and always tries to work and apply code in an IT infrastructure.

Источник

Discarding Output

If you’d like to discard unwanted output from commands, understanding and managing output streams is key. And if all else fails, you can always temporarily disable Out-Default.

When you write PowerShell scripts, you typically want to control the information sent back to the user. So when your script uses commands, you don’t necessarily want these commands to emit their output directly to the user.

Understanding Streams

In a perfect world, controlling and discarding command output is simple because commands typically use PowerShell streams to emit information, and you can redirect streams and assign them to variables or $null .

Suppressing Output Stream

For example, if all you want is creating a new folder, you can direct the output stream to $null and discard the information sent back by New-Item :

Showing Or Hiding Streams

In fact, there is a series of preference variables that allow you to selectively show or hide various streams:

Simply set a stream to Continue to make it visible, and to SilentlyContinue to hide it:

Each stream corresponds with Write-cmdlets and a stream number:

Stream Cmdlet Id
Debug Write-Debug 5
Error Write-Error 2
Information Write-Information 6
Verbose Write-Verbose 4
Warning Write-Warning 3
Output Write-Output 1
Host Write-Host 6

There are two streams that are not controlled by preference variables and are always visible: Output and Host.

The Output stream is the one that is piped to downstream cmdlets and assigned to variables. The Host stream shares the stream id 6 with the Information stream and can be silenced by redirecting stream 6.

Redirecting Streams

By default, only the Output stream is assigned to variables. All other streams either are emitted directly to the console, or are hidden (controlled by your preference variables).

To include the information from other streams in your variables, use redirection: take the stream id to redirect the stream to the output stream (&1).

This line redirects errors and warnings to the output stream and assigns them to a variable:

After you run it, $all contains the results plus all errors and warnings.

Special Case: Write-Host

Write-Host did not play by the rules and bypassed the stream system up until PowerShell 5 (thanks Justin for pointing out). Ever since, Write-Host is sharing the Information stream, and that’s important to know. Because of this, it is ok to use Write-Host again:

  • Write-Host : use this cmdlet for messages that a user should always see. Even though Write-Host is using the Information stream, it is not affected by any preference variable such as $InformationPreference , and always set to Continue.
  • Write-Information : use this cmdlet for messages that a user should not see by default ( $InformationPreference defaults to SilentlyContinue) but that a user might choose to turn on.

Here is a proof of concept illustrating how you can hide messages emitted by Write-Host :

By redirecting stream 6 to null, all outputs from Write-Host and Write-Information are discarded. You can append 6>$null to any command or script call.

While at it, there are two ways of discarding streams:

The first line redirects to $null which really is the same as redirecting to an empty string because the redirection operator takes only strings, so $null is converted to a string:

This is a special use case of the redirection operator. Typically, it expects a filename and writes the stream to this file. When you specify an empty string, the stream is nuked.

In contrast, the second line redirects the stream to the output stream (id #1) so the stream is merged into the output and can be assigned to variables, including the special variable $null .

This is a fundamentally different approach. The first line enables you to selectively discard streams that you want to hide. The second line enables you to assign or discard the entire mixture of output stream and any other stream you merged into the output stream.

Why Some Commands Are Ugly

Occasionally, you may stumble across commands that don’t play by the rules and write output that simply can’t be suppressed.

For example, Get-WindowsUpdateLog is a command on Windows 10 that extracts update information from .etl files and generates a log file for you:

When you run this command, it emits a ton of information, and there doesn’t seem to be a way to hide or discard this information:

Even if you redirect all streams (including stream 6) to the output stream and send everything to $null , the messages still show in the console.

Writing Directly To Host

This problem occurs whenever commands write directly to the console and bypass the stream mechanism. Here is a line of code that illustrates the problem:

Silencing All Output

To silence direct console output and discard all output, you can temporarily disable the internal PowerShell command Out-Default . This command is used privately to write directly to the console.

While you really can’t disable the command (it is hard-coded to PowerShell), you can replace it with your own functions, effectively overwriting it. So here is some example code that runs Get-WindowsUpdateLog without showing all the status messages:

As long as Out-Default is replaced by your own empty function, PowerShell emits no output whatsoever. This applies to scripts as well as interactive commands you may invoke. So make sure you remove your function Out-Default once you are ready to see output again.

This Out-Default trick isn’t as edge-casy as you may think. When you browse user groups you’ll discover plenty of threads that deal with this issue.

PowerShell users have resorted to all kinds of creative and complex workarounds to discard direct console output, i.e. running code as a job or in another PowerShell instance with a hidden window.

You know now: that’s a bit of overkill. Simply shadow Out-Default for as long as you want to silence PowerShell.

/
This Article is licensed under a Attribution-NoDerivatives 4.0 International license.
All Code samples are licensed under a Attribution 4.0 International license. Use, share, and experiment freely.

Источник

  1. Introduction to Error Action in PowerShell
  2. Use the -ErrorAction Parameter in PowerShell
  3. Setting Error Action Preferences in PowerShell

Suppressing PowerShell Errors

Whether we want to ignore error messages or terminate a script’s execution when an error occurs, Windows PowerShell has plenty of options for dealing with errors. This article will discuss multiple techniques in handling and suppressing errors.

Introduction to Error Action in PowerShell

Even though it is effortless to suppress Windows PowerShell errors, doing so isn’t always the best option (although it can be). If we carelessly tell PowerShell to hide errors, it can cause our script to behave unpredictably.

Suppressing error messages also makes troubleshooting and information gathering a lot more complicated. So tread lightly and be careful about using the following snippets that you will see in this article.

Use the -ErrorAction Parameter in PowerShell

The most common method for dealing with errors is to append the -ErrorAction parameter switch to a cmdlet. The -ErrorAction parameter switch lets PowerShell tell what to do if the cmdlet produces an error.

Command:

Get-Service 'svc_not_existing' -ErrorAction SilentlyContinue

In the command above, we are querying for a service that doesn’t exist. Usually, PowerShell will throw an error if the service doesn’t exist.

Since we use the -ErrorAction parameter, the script will continue as expected, like it doesn’t have an error.

Setting Error Action Preferences in PowerShell

If we need a script to behave in a certain way (such as suppressing errors), we might consider setting up some preference variables. Preference variables act as configuration settings for PowerShell.

We might use a preference variable to control the number of history items that PowerShell retains or force PowerShell to ask the user before performing specific actions.

For example, here is how you can use a preference variable to set the -ErrorAction parameter to SilentlyContinue for the entire session.

Command:

$ErrorActionPreference = 'SilentlyContinue'

There are many other error actions that we can specify for the ErrorAction switch parameter.

  • Continue: PowerShell will display the error message, but the script will continue to run.
  • Ignore: PowerShell does not produce any error message, writes any error output on the host, and continues execution.
  • Stop: PowerShell will display the error message and stop running the script.
  • Inquire: PowerShell displays the error message but will ask for confirmation first if the user wants to continue.
  • SilentlyContinue: PowerShell silently continues with code execution if the code does not work or has non-terminating errors.
  • Suspend: PowerShell suspends the workflow of the script.

As previously mentioned, the -ErrorAction switch has to be used in conjunction with a PowerShell cmdlet. For instance, we used the Get-Process cmdlet to demonstrate how the ErrorAction switch works.

  • Remove From My Forums
  • Question

  • Hello,

    I know there are various ways to suppress the ‘RED’ error info, using -ErrorAction SilentlyContinue, or trap{}, etc… but for the first time I have run into a scenario where I get this ‘YELLOW’ warning output… how do I suppress it?

    PS SQLSERVER:sql> (gi wrongInstanceName -ea silentlyContinue) -eq $null
    WARNING: Could not obtain SQL Server Service information. An attempt to connect to WMI on ‘wds-smqs’ failed with the following error: The RPC
    server is unavailable. (Exception from HRESULT: 0x800706BA)
    True

    in example above, that output is yellow, the red error output is suppressed with -ea SilentlyContinue. Is there a way to also suppress this yellow warning output? I will be checking for and handling error cases in my script
    and I would rather not have this warning be output.

Answers

    • Marked as answer by

      Wednesday, August 4, 2010 2:42 PM

  • Read all about it at

    I like to use only black text on white background for everything

    so I have this section in my profile.ps1

    <##>

    #

    # use black on white for all text

    # ———————————

    $Host.Ui.RawUi.BackGroundColor = «White»

    $Host.Ui.RawUi.ForeGroundColor = «Black»

    $host.privatedata.ErrorForegroundColor = «Black»

    $host.privatedata.ErrorBackgroundColor = «White»

    $host.privatedata.WarningForegroundColor = «Black»

    $host.privatedata.WarningBackgroundColor = «White»

    $host.privatedata.DebugForegroundColor = «Black»

    $host.privatedata.DebugBackgroundColor = «White»

    $host.privatedata.VerboseForegroundColor = «Black»

    $host.privatedata.VerboseBackgroundColor = «White»

    $host.privatedata.ProgressForegroundColor = «Black»

    $host.privatedata.ProgressBackgroundColor = «White»

    <##>

    • Marked as answer by
      c0pe
      Tuesday, August 3, 2010 4:12 PM

This section briefly demonstrates how to use each of PowerShell’s statements, variables and parameters that are related to the reporting or handling of errors.

$Error is an automatic global variable in PowerShell which always contains an ArrayList of zero or more ErrorRecord objects. As new errors occur, they are added to the beginning of this list, so you can always get information about the most recent error by looking at $Error[0]. Both Terminating and Non-Terminating errors will be contained in this list.

Aside from accessing the objects in the list with array syntax, there are two other common tasks that are performed with the $Error variable: you can check how many errors are currently in the list by checking the $Error.Count property, and you can remove all errors from the list with the $Error.Clear() method. For example:

Figure 2.1: Using $Error to access error information, check the count, and clear the list.

If you’re planning to make use of the $Error variable in your scripts, keep in mind that it may already contain information about errors that happened in the current PowerShell session before your script was even started. Also, some people consider it a bad practice to clear the $Error variable inside a script; since it’s a variable global to the PowerShell session, the person that called your script might want to review the contents of $Error after it completes.

The ErrorVariable common parameter provides you with an alternative to using the built-in $Error collection. Unlike $Error, your ErrorVariable will only contain errors that occurred from the command you’re calling, instead of potentially having errors from elsewhere in the current PowerShell session. This also avoids having to clear the $Error list (and the breach of etiquette that entails.)

When using ErrorVariable, if you want to append to the error variable instead of overwriting it, place a + sign in front of the variable’s name. Note that you do not use a dollar sign when you pass a variable name to the ErrorVariable parameter, but you do use the dollar sign later when you check its value.

The variable assigned to the ErrorVariable parameter will never be null; if no errors occurred, it will contain an ArrayList object with a Count of 0, as seen in figure 2.2:

Figure 2.2: Demonstrating the use of the ErrorVariable parameter.

By default, the $Error variable can only contain a maximum of 256 errors before it starts to lose the oldest ones on the list. You can adjust this behavior by modifying the $MaximumErrorCount variable.

ErrorAction and $ErrorActionPreference

There are several ways you can control PowerShell’s handling / reporting behavior. The ones you will probably use most often are the ErrorAction common parameter and the $ErrorActionPreference variable.

The ErrorAction parameter can be passed to any Cmdlet or Advanced Function, and can have one of the following values: Continue (the default), SilentlyContinue, Stop, Inquire, Ignore (only in PowerShell 3.0 or later), and Suspend (only for workflows; will not be discussed further here.) It affects how the Cmdlet behaves when it produces a non-terminating error.

  • The default value of Continue causes the error to be written to the Error stream and added to the $Error variable, and then the Cmdlet continues processing.

  • A value of SilentlyContinue only adds the error to the $Error variable; it does not write the error to the Error stream (so it will not be displayed at the console).

  • A value of Ignore both suppresses the error message and does not add it to the $Error variable. This option was added with PowerShell 3.0.

  • A value of Stop causes non-terminating errors to be treated as terminating errors instead, immediately halting the Cmdlet’s execution. This also enables you to intercept those errors in a Try/Catch or Trap statement, as described later in this section.

  • A value of Inquire causes PowerShell to ask the user whether the script should continue or not when an error occurs.

The $ErrorActionPreference variable can be used just like the ErrorAction parameter, with a couple of exceptions: you cannot set $ErrorActionPreference to either Ignore or Suspend. Also, $ErrorActionPreference affects your current scope in addition to any child commands you call; this subtle difference has the effect of allowing you to control the behavior of errors that are produced by .NET methods, or other causes such as PowerShell encountering a «command not found» error.

Figure 2.3 demonstrates the effects of the three most commonly used $ErrorActionPreference settings.

Figure 2.3: Behavior of $ErrorActionPreference

The Try/Catch/Finally statements, added in PowerShell 2.0, are the preferred way of handling terminating errors. They cannot be used to handle non-terminating errors, unless you force those errors to become terminating errors with ErrorAction or $ErrorActionPreference set to Stop.

To use Try/Catch/Finally, you start with the «Try» keyword followed by a single PowerShell script block. Following the Try block can be any number of Catch blocks, and either zero or one Finally block. There must be a minimum of either one Catch block or one Finally block; a Try block cannot be used by itself.

The code inside the Try block is executed until it is either complete, or a terminating error occurs. If a terminating error does occur, execution of the code in the Try block stops. PowerShell writes the terminating error to the $Error list, and looks for a matching Catch block (either in the current scope, or in any parent scopes.) If no Catch block exists to handle the error, PowerShell writes the error to the Error stream, the same thing it would have done if the error had occurred outside of a Try block.

Catch blocks can be written to only catch specific types of Exceptions, or to catch all terminating errors. If you do define multiple catch blocks for different exception types, be sure to place the more specific blocks at the top of the list; PowerShell searches catch blocks from top to bottom, and stops as soon as it finds one that is a match.

If a Finally block is included, its code is executed after both the Try and Catch blocks are complete, regardless of whether an error occurred or not. This is primarily intended to perform cleanup of resources (freeing up memory, calling objects’ Close() or Dispose() methods, etc.)

Figure 2.4 demonstrates the use of a Try/Catch/Finally block:

Figure 2.4: Example of using try/catch/finally.

Notice that «Statement after the error» is never displayed, because a terminating error occurred on the previous line. Because the error was based on an IOException, that Catch block was executed, instead of the general «catch-all» block below it. Afterward, the Finally executes and changes the value of $testVariable.

Also notice that while the Catch block specified a type of [System.IO.IOException], the actual exception type was, in this case, [System.IO.DirectoryNotFoundException]. This works because DirectoryNotFoundException is inherited from IOException, the same way all exceptions share the same base type of System.Exception. You can see this in figure 2.5:

Figure 2.5: Showing that IOException is the Base type for DirectoryNotFoundException

Trap statements were the method of handling terminating errors in PowerShell 1.0. As with Try/Catch/Finally, the Trap statement has no effect on non-terminating errors.

Trap is a bit awkward to use, as it applies to the entire scope where it is defined (and child scopes as well), rather than having the error handling logic kept close to the code that might produce the error the way it is when you use Try/Catch/Finally. For those of you familiar with Visual Basic, Trap is a lot like «On Error Goto». For that reason, Trap statements don’t see a lot of use in modern PowerShell scripts, and I didn’t include them in the test scripts or analysis in Section 3 of this ebook.

For the sake of completeness, here’s an example of how to use Trap:

Figure 2.6: Use of the Trap statement

As you can see, Trap blocks are defined much the same way as Catch blocks, optionally specifying an Exception type. Trap blocks may optionally end with either a Break or Continue statement. If you don’t use either of those, the error is written to the Error stream, and the current script block continues with the next line after the error. If you use Break, as seen in figure 2.5, the error is written to the Error stream, and the rest of the current script block is not executed. If you use Continue, the error is not written to the error stream, and the script block continues execution with the next statement.

The $LASTEXITCODE Variable

When you call an executable program instead of a PowerShell Cmdlet, Script or Function, the $LASTEXITCODE variable automatically contains the process’s exit code. Most processes use the convention of setting an exit code of zero when the code finishes successfully, and non-zero if an error occurred, but this is not guaranteed. It’s up to the developer of the executable to determine what its exit codes mean.

Note that the $LASTEXITCODE variable is only set when you call an executable directly, or via PowerShell’s call operator (&) or the Invoke-Expression cmdlet. If you use another method such as Start-Process or WMI to launch the executable, they have their own ways of communicating the exit code to you, and will not affect the current value of $LASTEXITCODE.

Figure 2.7: Using $LASTEXITCODE.

The $? variable is a Boolean value that is automatically set after each PowerShell statement or pipeline finishes execution. It should be set to True if the previous command was successful, and False if there was an error.
When the previous command was a PowerShell statement, $? will be set to False if any errors occurred (even if ErrorAction was set to SilentlyContinue or Ignore).
If the previous command was a call to a native exe, $? will be set to False if the $LASTEXITCODE variable does not equal zero. If the $LASTEXITCODE variable equals zero, then in theory $? should be set to True. However, if the native command writes something to the standard error stream then $? could be set to False even if $LASTEXITCODE equals zero. For example, the PowerShell ISE will create and append error objects to $Error for standard error stream output and consequently $? will be False regardless of the value of $LASTEXITCODE. Redirecting the standard error stream into a file will result in a similar behavior even when the PowerShell host is the regular console. So it is probably best to test the behavior in your specific environment if you want to rely on $? being set correctly to True.

Just be aware that the value of this variable is reset after every statement. You must check its value immediately after the command you’re interested in, or it will be overwritten (probably to True). Figure 2.8 demonstrates this behavior. The first time $? is checked, it is set to False, because the Get-Item encountered an error. The second time $? was checked, it was set to True, because the previous command was successful; in this case, the previous command was «$?» from the first time the variable’s value was displayed.

Figure 2.8: Demonstrating behavior of the $? variable.

The $? variable doesn’t give you any details about what error occurred; it’s simply a flag that something went wrong. In the case of calling executable programs, you need to be sure that they return an exit code of 0 to indicate success and non-zero to indicate an error before you can rely on the contents of $?.

That covers all of the techniques you can use to either control error reporting or intercept and handle errors in a PowerShell script. To summarize:

  • To intercept and react to non-terminating errors, you check the contents of either the automatic $Error collection, or the variable you specified as the ErrorVariable. This is done after the command completes; you cannot react to a non-terminating error before the Cmdlet or Function finishes its work.

  • To intercept and react to terminating errors, you use either Try/Catch/Finally (preferred), or Trap (old and not used much now.) Both of these constructs allow you to specify different script blocks to react to different types of Exceptions.

  • Using the ErrorAction parameter, you can change how PowerShell cmdlets and functions report non-terminating errors. Setting this to Stop causes them to become terminating errors instead, which can be intercepted with Try/Catch/Finally or Trap.

  • $ErrorActionPreference works like ErrorAction, except it can also affect PowerShell’s behavior when a terminating error occurs, even if those errors came from a .NET method instead of a cmdlet.

  • $LASTEXITCODE contains the exit code of external executables. An exit code of zero usually indicates success, but that’s up to the author of the program.

  • $? can tell you whether the previous command was successful, though you have to be careful about using it with external commands. If they don’t follow the convention of using an exit code of zero as an indicator of success or if they write to the standard error stream then the resulting value in $? may not be what you expect. You also need to make sure you check the contents of $? immediately after the command you are interested in.

When you write PowerShell scripts, you typically want to control the information sent back to the user. So when your script uses commands, you don’t necessarily want these commands to emit their output directly to the user.

Understanding Streams

In a perfect world, controlling and discarding command output is simple because commands typically use PowerShell streams to emit information, and you can redirect streams and assign them to variables or $null.

Suppressing Output Stream

For example, if all you want is creating a new folder, you can direct the output stream to $null and discard the information sent back by New-Item:

# create a new folder and discard returned information
$null = New-Item -Path 'c:newfolder' -ItemType Directory

Showing Or Hiding Streams

In fact, there is a series of preference variables that allow you to selectively show or hide various streams:

Get-Variable -Name *preference -Exclude *confirm*,*whatif*,*progress*   

Simply set a stream to Continue to make it visible, and to SilentlyContinue to hide it:

Name                  Value
----                  -----
DebugPreference       SilentlyContinue
ErrorActionPreference Continue
InformationPreference SilentlyContinue
VerbosePreference     SilentlyContinue
WarningPreference     Continue

Each stream corresponds with Write-cmdlets and a stream number:

Stream Cmdlet Id
Debug Write-Debug 5
Error Write-Error 2
Information Write-Information 6
Verbose Write-Verbose 4
Warning Write-Warning 3
Output Write-Output 1
Host Write-Host 6

There are two streams that are not controlled by preference variables and are always visible: Output and Host.

The Output stream is the one that is piped to downstream cmdlets and assigned to variables. The Host stream shares the stream id 6 with the Information stream and can be silenced by redirecting stream 6.

Redirecting Streams

By default, only the Output stream is assigned to variables. All other streams either are emitted directly to the console, or are hidden (controlled by your preference variables).

To include the information from other streams in your variables, use redirection: take the stream id to redirect the stream to the output stream (&1).

This line redirects errors and warnings to the output stream and assigns them to a variable:

# include errors and warnings in variable results:
$all = Get-Process -FileVersionInfo 2>&1 3>&1

After you run it, $all contains the results plus all errors and warnings.

Special Case: Write-Host

Write-Host did not play by the rules and bypassed the stream system up until PowerShell 5 (thanks Justin for pointing out). Ever since, Write-Host is sharing the Information stream, and that’s important to know. Because of this, it is ok to use Write-Host again:

  • Write-Host: use this cmdlet for messages that a user should always see. Even though Write-Host is using the Information stream, it is not affected by any preference variable such as $InformationPreference, and always set to Continue.
  • Write-Information: use this cmdlet for messages that a user should not see by default ($InformationPreference defaults to SilentlyContinue) but that a user might choose to turn on.

Here is a proof of concept illustrating how you can hide messages emitted by Write-Host:

& {
Write-Warning "A Warning"
"Regular Output"
Write-Host "This will dissappear!"
} 6>$null

By redirecting stream 6 to null, all outputs from Write-Host and Write-Information are discarded. You can append 6>$null to any command or script call.

While at it, there are two ways of discarding streams:

# two ways of discarding streams:
Write-Warning "A Warning" 3>$null
$null = Write-Warning "A Warning" 3>&1

The first line redirects to $null which really is the same as redirecting to an empty string because the redirection operator takes only strings, so $null is converted to a string:

Write-Warning "A Warning" 3>''

This is a special use case of the redirection operator. Typically, it expects a filename and writes the stream to this file. When you specify an empty string, the stream is nuked.

In contrast, the second line redirects the stream to the output stream (id #1) so the stream is merged into the output and can be assigned to variables, including the special variable $null.

This is a fundamentally different approach. The first line enables you to selectively discard streams that you want to hide. The second line enables you to assign or discard the entire mixture of output stream and any other stream you merged into the output stream.

Why Some Commands Are Ugly

Occasionally, you may stumble across commands that don’t play by the rules and write output that simply can’t be suppressed.

For example, Get-WindowsUpdateLog is a command on Windows 10 that extracts update information from .etl files and generates a log file for you:

When you run this command, it emits a ton of information, and there doesn’t seem to be a way to hide or discard this information:

# redirecting streams won't suppress the output
$null = Get-WindowsUpdateLog *>&1

Even if you redirect all streams (including stream 6) to the output stream and send everything to $null, the messages still show in the console.

Writing Directly To Host

This problem occurs whenever commands write directly to the console and bypass the stream mechanism. Here is a line of code that illustrates the problem:

# cannot be silenced:
& { [Console]::WriteLine("Hello") } *>$null

Silencing All Output

To silence direct console output and discard all output, you can temporarily disable the internal PowerShell command Out-Default. This command is used privately to write directly to the console.

Read more about Out-Default if you like.

While you really can’t disable the command (it is hard-coded to PowerShell), you can replace it with your own functions, effectively overwriting it. So here is some example code that runs Get-WindowsUpdateLog without showing all the status messages:

# temporarily overwrite Out-Default
function Out-Default {}

# run your code (guaranteed no output)
Get-WindowsUpdateLog

# test any other direct console write
[Console]::WriteLine("Hello")

# restore Out-Default
Remove-Item -Path function:Out-Default

As long as Out-Default is replaced by your own empty function, PowerShell emits no output whatsoever. This applies to scripts as well as interactive commands you may invoke. So make sure you remove your function Out-Default once you are ready to see output again.

This Out-Default trick isn’t as edge-casy as you may think. When you browse user groups you’ll discover plenty of threads that deal with this issue.

PowerShell users have resorted to all kinds of creative and complex workarounds to discard direct console output, i.e. running code as a job or in another PowerShell instance with a hidden window.

You know now: that’s a bit of overkill. Simply shadow Out-Default for as long as you want to silence PowerShell.

Понравилась статья? Поделить с друзьями:
  • Powershell on error resume next
  • Powershell logging error
  • Powershell last error
  • Powershell get error code
  • Powershell generate error