Posh Security

View Original

Crossing the PowerShell streams

I was recently working with the PowerCat code which was capturing the Pipeline and Error output of code, and wondered, could I also capture the other messages being displayed? Why couldn’t I also redirect the warnings or the verbose output as well?

Let’s revisit how the redirection of the streams works in PowerShell, and hopefully learn about how PowerShell does things under the covers. 

To explore the output streams, I am going to use the CMDLet I have below, which is a varation of the one June Blender used in her post on this topic.

See this content in the original post

It should also be noted that redirection to a variable is similar as to a file. In my examples I am redirecting to a variable.

So what happens if we run this CMDLet normally? We should see four distinct messages, first is out the text we are displaying by just returning a string, next we will have our write-output, then we have write-warning, and finally we will have the wrote-error message (and its associated trace).

What happens if we decide to assign the output to a variable called $output? Let’s take a look.

As you can see, after running the function, we still saw the warning and error messages. What was stored in the variable? Well if we look at that we can see that the text return and the write-output were successfully assigned to the variable $output.

Ok, so how about we apply the old redirection rules that have existed in every shell since the 80s?

Well this is much as we expected, but wait, the warning was displayed and not captured in the variable. The variable has our two output strings and it also has the write-error message as well. As a side note I love how the when calling $output PowerShell still displays the error in red.

If you are running PowerShell 2.0 (or less), this is the end of the line for you. There is no help for you.

If you are running PowerShell 3.0 (or greater), then how do we capture that warning message? What about the verbose?

With the introduction of PowerShell 3.0, Microsoft included support for capturing the other streams, and they did so in a method which is simple, clean and logical. Microsoft simply extended the stream redirection along the well-known and practiced methods.

Let’s take a look at the streams in PowerShell 3.0:

See this content in the original post

Let’s take a look at the previous example again, however this time we will redirect the output of stream 3, warning, to stream 1. 

As you can see, we captured this information just as expected.

Let’s try verbose output now.

We can see that simply using 4>&1 captures the output. We can also capture all output using the wildcard, *>&1.

I can hear one person asking, “But what about write-host?”

There are many reasons why you should not use write-host, firstly, Jeffrey Snover, the creator of PowerShell says not to, Don Jones, a PowerShell MVP says “Write-host kills puppies” <link>, however both agree, as does the PowerShell community, that there are some times when it might be handy to use Write-Host. Write-Host is black PowerShell magic, it’s the dark side of PowerShell. It is not something you should do without very thoroughly thinking about what you are about to do.

The only legitimate time to use Write-Host, is when you do not want to interrupt or pollute your stream. If you have a situation where you wish to display text to the user in such a way that it will not be caught up in the pipeline. 

I only use Write-Host in one piece of code. I use it as part of my modular alerting framework, where I know that the message needs to be seen by the actual user of the code. I also didn’t want event/alert messages contaminating any of my other pieces of code. When I wrote the code, I knew it was a risky piece of code, however it has a clear cut function and purpose. 

So what happens to Write-Host and redirected streams? To show you, I have added a Write-Host line to our previous CMDLet as show below:

See this content in the original post

Let’s run the code, redirecting all input (*>&1) into $output.

As you can see, the write-host was still displayed, everything else was captured in $output.
So what information is around on stream redirection? There are three good resources, firstly, check out June’s post on Hey Scripting Guy, secondly, PS> Get-Help about_Redirection and finally, there is the Microsoft Connect entry where the functionality was requested.

Kieran

PS. No streams were crossed during the creation of this post.

See this social icon list in the original post