Windows PowerShell Tutorial 5 - Advanced Redirection

Advanced Redirection and the Cmdlet Out-File



  1. Regedit32
    In the previous tutorial I introduced to you the basic concept of Redirection and Piping of information via Cmdlets to an eternal file.

    PowerShell by default writes the actual Script to the console while executing it, but mote importantly by default will also write command output to the console. Often this is perfectly fine, but there are many occasions where it is better in fact to write command output to a external file which can be more easily analyzed and shared with others should there be a need for this.

    Examples of when writing output to an external file may be more useful are when you wish to save text output for review later on, or wish to record specific error messages that may help you resolve an issue with your Operating System or some other application.

    If you read Tutorial 4 you will be aware you can pipe output from a Cmdlet then redirect it to a file easily using the following format:

    Code (PowerShell):
    Cmdlet | Cmdlet { some parameters & arguments } > $env:Some_Location\File.txt
    So the Cmdlet on left side of pipe produces an object which is then piped to another Cmdlet which receives that object as input, then creates a new object which is then redirected to an external file.

    While this basic redirection can be a little confusing at first to the novice programmer, unfortunately, thing only get trickier for more advanced Scripting in PowerShell owing to the fact PowerShell has multiple ways to Stream a message as output.
    1. Write-Host
    2. Write-Verbose
    3. Write-Warning
    4. Write-Error
    5. Write-Debug
    Each of these streaming methods can run parallel to another Stream and are sending different messages. They will not conflict with each other if you are Scripting error controls into your code.

    Taking this into account PowerShell has a way for you to indicate more specifically the type of redirection you are intending when Scripting the redirection.

    I'll get to that shortly, but first let's try using a simple Function to take a look at what each of the above Stream options will do if we only apply the basic Redirect all ready explained in the previous Tutorial or just some of the more advanced redirects not yet explained in full.

    Note: You will be creating .txt files in the default directory while trying this function, and will also be prompted to click Yes or Yes to all, two or three times during its execution. Any files created can be safely deleted when you've completed this Tutorial if you wish.

    Code (PowerShell):

    #  CREDIT:  'The Scripting Guys', Ed Wilson
    #  Website:  https://blogs.technet.microsoft.com/heyscriptingguy/2014/03/30/understanding-streams-redirection-and-write-host-in-powershell/

    function Write-Messages {
      [CmdletBinding()]
      param()
      Write-Host "`nHost message`n"
      Write-Output "`nOutput message`n"
      Write-Verbose "`nVerbose message`n"
      Write-Warning "`nWarning message`n"
      Write-Error "`nError message`n"
      Write-Debug "`nDebug message`n"
    }
    # Writes all messages to console.
    Write-Messages -Verbose -Debug

    # Writes output to the .txt file in the current active directory
    # Writes all other messages to console.
    Write-Messages -Verbose -Debug > .\OutputFile.txt

    # Writes all output except Host messages to .txt file in the current active directory
    Write-Messages -Verbose -Debug *> .\OutputFile.txt

    # Writes all output (except Host messages) to output stream,
    # then saves them in a .txt file in Current active directory.
    Write-Messages -Verbose -Debug *>&1 | Out-File -FilePath .\OutputFile.txt

    # Writes all messages to console and then saves all but host messages in a .txt file,
    # in the current active directory.
    Write-Messages -Verbose -Debug *>&1 | Tee-Object -FilePath .\OutputFile.txt
     
    Copy & paste the above into the Script pane in your Windows PowerShell ISE then click File > Run.

    PS33.png

    As you can see these different types of streamed messages will display on your console in a different colour. This is by design, and if you wanted to you could manipulate the colours using -ForeGroundColor green or what ever colour you want the message displayed in. Obviously, given the default console colour is a mid-dark blue, unless you manually change that colour by the options settings, you will want to select a colour that will be legible on the console.

    These same messages being saved to a .txt file will not appear in different colours though. You can confirm that by typing notepad.exe Outputfile.txt into the Windows PowerShell ISE and pressing Enter key

    PS34.png


    So why the need for Advanced Redirections?

    Streams are critical in any language that uses piping.

    In PowerShell as already explained you can pipe one object from a Cmdlet to be used as input for another Cmdlet. However, for the receiving Cmdlet to be able to action this input you do not want that receiving Cmdlet to receive errors, warnings, debugging messages, or verbose messages along with the objects that it's designed to process. If it does, its likely to bring your Script to a crashing halt and leave you with some serious debugging to do in order to get your Script to do what you intended it to be doing.

    In PowerShell when you use | (the pipe operator), it is actually piping an object down the output stream. The receiving Cmdlet does not see errors, warnings, verbose messages, or debugging messages, because they are sent in other streams.

    Windows PowerShell also uses the output stream for assignments, like assigning (i.e. saving data) a value in a variable. This is why you can save an output message in a variable, for example:

    Code (PowerShell):
    $message = Write-Output -Message "Output message"

    $message
    PS35.png


    However, it is not possible to assign a verbose message to a variable in this way. Look what would happen if you tried using this code:


    Code (PowerShell):
    $message = Write-Verbose -Message "Verbose message" -Verbose VERBOSE: Verbose message

    $message
    PS36.png
    Attempting to do the impossible results in one heck of an error message!

    As a result Microsoft provided some more advanced methods to redirect a Stream message.

    So if we relook at our last attempt to save a Verbose message, but apply an advanced redirect instead, then look what happens now.

    Code (PowerShell):
    $message = Write-Verbose -Message "Verbose message" -Verbose 4>&1

    $message


    PS37.png
    The impossible has now become possible by using the advanced redirect 4>&1

    It's a little difficult to explain this in words, so let's make use of that saying, 'A picture tells a thousand words' — actually two pictures in this case.

    Illustration displaying the 5 Stream options in Windows PowerShell
    Image1.png

    Image displaying what is happening when using advanced redirect 4>&1
    Image2.png


    Note: You can apply this same thinking to any of the advanced redirect options.


    So what are the Advanced Redirect Options?

    As there are 5 streams there are naturally five advanced redirect choices plus one extra choice to allow you to redirect all five outputs.

    Below is a table showing you these choices:
    Image3.png

    Below is some illustrations of Advanced redirection, and examples of its use:

    Code (PowerShell):
    Operator  Description                Example   [/B][/B][/B]
    [B][B][B]--------  ----------------------     ------------------------------
    >         Sends output to the        Get-Process > Process.txt
              specified file.

    >>        Appends the output to      dir *.ps1 >> Scripts.txt
              the contents of the
              specified file.

    2>        Sends errors to the        Get-Process none 2> Errors.txt
              specified file.

    2>>       Appends errors to          Get-Process none 2>> Save-Errors.txt
              the contents of the
              specified file.

    2>&1      Sends errors (2) and       Get-Process none, Powershell 2>&1
              success output (1)
              to the success
              output stream.

    3>        Sends warnings to the      Write-Warning "Test!" 3> Warnings.txt
              specified file.

    3>>       Appends warnings to        Write-Warning "Test!" 3>> Save-Warnings.txt
              the contents of the
              specified file.

    3>&1      Sends warnings (3) and     function Test-Warning
              success output (1)         {  Get-Process PowerShell;
              to the success                Write-Warning "Test!" }
              output stream.             Test-Warning 3>&1

    4>        Sends verbose output to    Import-Module * -Verbose 4> Verbose.txt
              the specified file.

    4>>       Appends verbose output     Import-Module * -Verbose 4>> Save-Verbose.txt
              to the contents of the
              specified file.

    4>&1      Sends verbose output (4)   Import-Module * -Verbose 4>&1
              and success output (1)  
              to the success output
              stream.            

    5>        Sends debug messages to    Write-Debug "Starting" 5> Debug.txt
              the specified file.

    5>>       Appends debug messages     Write-Debug "Saving" 5>> Save-Debug.txt
              to the contents of the
              specified file.

    5>&1      Sends debug messages (5)   function Test-Debug
              and success output (1)     { Get-Process PowerShell
              to the success output        Write-Debug "PS" }
              stream.                    Test-Debug 5>&1

    *>        Sends all output types     function Test-Output
              to the specified file.     { Get-Process PowerShell, none
                                           Write-Warning "Test!"
    *>>       Appends all output types     Write-Verbose "Test Verbose"
              to the contents of the       Write-Debug "Test Debug" }
              specified file.          
                                         Test-Output *> Test-Output.txt
    *>&1      Sends all output types     Test-Output *>> Test-Output.txt
              (*) to the success output  Test-Output *>&1    
              stream.


    Note: More information on this can be found here at the following online resource:

    https://docs.microsoft.com/en-us/po...ll.Core/about_Redirection?view=powershell-5.1

    Before we go, one last important note.

    Earlier in this tutorial I showed you the two examples of messages being streamed to the console and/or .txt files. In the initial example, all messages reached the console, with one exception that was sent to the OutputFile.txt.

    Then in the second example by using the redirect 4>&1 we could store data in a variable and print to the console.

    Now if you were to make use of the function in those examples, to attempt the following example you would see that all messages would be sent to the OutputFile.txt with the exception of the Write-Host message.

    Code (PowerShell):
    Write-Messages *> .\OutputFile.txt

    The above code uses the advanced All Output redirect *>

    The Cmdlet Write-Host does not in fact get written to any of the Stream options mentioned in this Tutorial. It simply sends the collected data, or attempts to send it, to the Console of either Windows PowerShell ISE, or Windows PowerShell subject obviously to which console you are using at the time.

    Thus, you cannot redirect Write-Host messages so if you wish to communicate all streamed messages to the User, you are best to use Write-Verbose as we did in the second example.

    Well that brings us to the conclusion of a rather tricky subject. Don't panic if it has not all sunk in yet. The learning curve on this topic is one of those things that really only improves as you apply the techniques in your Scripting.

    If you cannot remember all the special items — in this case the 6 ways to do advanced redirection, again, do not panic, as the online resource mentioned in the Tutorial is not going anywhere, so you can easily bookmark it, or if you want print it out and save it.

    In the next Tutorial I'll be addressing the user or Operators in PowerShell within arithmetic cases as well as with Commands and Strings. Something a little lighter to study, unless you hate math that is :D


    Regards,

    Regedit32

    Images

    1. upload_2017-8-10_17-51-14.png