Hey, Scripting Guy! How Can I Both Save Information in a File and Display It on the Screen?

Hey, Scripting Guy! How Can I Both Save Information in a File and Display It on the Screen?

  • Comments 1
  • Likes

Hey, Scripting Guy! Question

Hey, Scripting Guy! You know the old commercial that goes, "Sometimes you feel like a nut, sometimes you don't"? Well, sometimes I want to save information from a script to a file, and sometimes I want to see it displayed to the screen. And at other times, I want both. In other words, I want my candy bar, and I want to eat it, too.

- NR

SpacerHey, Scripting Guy! Answer

Hi NR,

I never did really get the expression, "I want my cake and I want to eat it too." I mean, what good is cake if you cannot eat it? Or for that matter, who would want a cake that they only used for decoration? To me it doesn't make sense. I think there is art, and there is food. Food is to be consumed, and art is to be appreciated but not eaten. When I go to the Guggenheim in New York City, I go there to nourish my soul, not my belly. When I go to the Sydney Opera House, seen in the following picture I took during my last trip to Australia, I can actually do both because there is an excellent restaurant under the front sail.

Image of the Sydney Opera House

If you want to display information on the screen, you run the command and by default it will send the information to the console. This is seen here:

PS C:\> Get-WmiObject -Class win32_bios


SMBIOSBIOSVersion : A01
Manufacturer      : Dell Computer Corporation
Name              : Default System BIOS
SerialNumber      : 9HQ1S21
Version           : DELL   - 6



PS C:\>

If you want to store the information in a text file, you can use the redirection arrow as seen here:

PS C:\> Get-WmiObject -Class win32_bios >c:\fso\bios.txt

PS C:\>

The problem is there is neither a confirmation message that the command completed successfully, nor an idea of what is in the text file. There is a cmdlet you can use—the Out-File cmdlet. But once again, as seen here, there is no feedback from the command:

PS C:\> Get-WmiObject -Class win32_bios | Out-File -FilePath C:\fso\bios.txt
PS C:\>

One thing you could do is to use the Get-Content cmdlet to inspect the contents of the file to ensure it has the information you require. The thing to keep in mind is that you are not piping the information from the Out-File cmdlet to the Get-Content cmdlet. The semicolon is used to indicate that you are beginning a new command. The semicolon is the equivalent of typing the command on a new line in a script. This is seen here:

PS C:\> Get-WmiObject -Class win32_bios | Out-File -FilePath C:\fso\bios.txt ;
Get-Content -Path C:\fso\bios.txt


SMBIOSBIOSVersion : A01
Manufacturer      : Dell Computer Corporation
Name              : Default System BIOS
SerialNumber      : 9HQ1S21
Version           : DELL   - 6

Because you have already seen that the redirection arrow is the same as using the Out-File cmdlet, for your purposes here you can revise the command to use the redirection arrows. You can also shorten the command a bit by using the alias cat instead of the longer Get-Content cmdlet name. This command is seen here:

PS C:\> Get-WmiObject -Class win32_bios > C:\fso\bios.txt ; cat C:\fso\bios.txt



SMBIOSBIOSVersion : A01
Manufacturer      : Dell Computer Corporation
Name              : Default System BIOS
SerialNumber      : 9HQ1S21
Version           : DELL   - 6

By using an alias for the Get-WmiObject cmdlet and also leaving out the class parameter name, you can create a short command:

PS C:\> gwmi win32_bios > C:\fso\bios.txt ; cat C:\fso\bios.txt


SMBIOSBIOSVersion : A01
Manufacturer      : Dell Computer Corporation
Name              : Default System BIOS
SerialNumber      : 9HQ1S21
Version           : DELL   - 6

Now you have a shorter command that you can use to feed the content from the command to a text file for storage, and then display the information on the console. Though this is a workable solution, it would be easier if there was a cmdlet that would essentially do this for us. As it turns out, there is a cmdlet that will split the output from a cmdlet and direct it to both the screen and to a file. The cmdlet is called the Tee-Object. Most of the time, you will split the output from your command line to a file and to the console. To do this, you will use the filepath parameter and specify the full path to the file. As seen here, the Tee-Object cmdlet supports a number of additional switches and parameters.

Tee-Object [-FilePath] <String> [-InputObject <PSObject>] [-Verbose] [-Debug]
[-ErrorAction <ActionPreference>] [-ErrorVariable <String>] [-OutVariable
<String>] [-OutBuffer <Int32>]

To return to the example, you can replace the redirection arrow (or the Out-File cmdlet) and the Get-Content cmdlet (cat alias) with the Tee-Object cmdlet. The revised code is seen here:

Get-WmiObject -Class win32_bios | Tee-Object -FilePath c:\fso\bios.txt

When you run the command, you receive the output shown here:

Image of the output received


One thing to keep in mind when using the Tee-Object is that it will always overwrite the previous text file if the file already exists. If the file does not exist, the Tee-Object cmdlet will create the file. The Tee-Object cmdlet will not create the folder. If you attempt to use the Tee-Object cmdlet to write to a folder that does not exist, you will receive an error that warns of a missing path. This error message is seen here:

PS C:\> Get-WmiObject -class win32_bios | Tee-Object -FilePath C:\fso5\bios.txt
out-file : Could not find a part of the path 'C:\fso5\bios.txt'.
PS C:\>

You can also use the Tee-Object cmdlet to hold the output of a command in a variable. This offers a convenient way to save the information for use later in the script. Here is how you would save the results of a command in a variable, and then display it later without using the Tee-Object:

PS C:\> $bios = Get-WmiObject -class win32_bios
PS C:\> $bios

SMBIOSBIOSVersion : A01
Manufacturer      : Dell Computer Corporation
Name              : Default System BIOS
SerialNumber      : 9HQ1S21
Version           : DELL   - 6

PS C:\>

The syntax for the Tee-Object when used to store the results of a pipeline in a variable is seen here:

Tee-Object [-InputObject <PSObject>] -Variable <String> [-Verbose] [-Debug]
[-ErrorAction <ActionPreference>] [-ErrorVariable <String>] [-OutVariable
<String>] [-OutBuffer <Int32>]

To store the results of your Get-WmiObject –Class Win32_Bios command in a variable named $bios, you use the following command:

PS C:\> Get-WmiObject -class win32_bios | Tee-Object -Variable bios


SMBIOSBIOSVersion : A01
Manufacturer      : Dell Computer Corporation
Name              : Default System BIOS
SerialNumber      : 9HQ1S21
Version           : DELL   - 6

The one thing you need to keep in mind with the Tee-Object cmdlet when using the variable parameter is that you do not need to use a dollar sign in front of the variable name. This makes the behavior of the cmdlet the same as when using the New-Variable cmdlet.

To see the contents of the $bios variable, you type it on the command line in the Windows PowerShell console as seen here:

PS C:\> $bios

SMBIOSBIOSVersion : A01
Manufacturer      : Dell Computer Corporation
Name              : Default System BIOS
SerialNumber      : 9HQ1S21
Version           : DELL   - 6

PS C:\>

One of the best features of the Tee-Object is that it will also pass the object though the pipeline. This means you are not stuck with the default display of information that is returned by the previous command such as the Get-WmiObject cmdlet. You can store the object in the $bios variable and then choose to display only the name property:

PS C:\> Get-WmiObject -class win32_bios | Tee-Object -Variable bios |

select name

name
----
Default System BIOS

To retrieve the object from the variable, you once again type it on the command line or use it elsewhere in your script. This is seen here:

PS C:\> $bios

SMBIOSBIOSVersion : A01
Manufacturer      : Dell Computer Corporation
Name              : Default System BIOS
SerialNumber      : 9HQ1S21
Version           : DELL   - 6

Well, NR, we have come to the end of our discussion on splitting the output from commands to the command line and storing it in a file. In addition, we decided to show you a cool trick that allows you to store objects in variables and manipulate that output as well. Hopefully you have enjoyed this discussion. Join us tomorrow as we continue looking at handling output from within Windows PowerShell. To keep up to date with all the latest Script Center news, follow us on Twitter. You can also check out the Scripting Guys on Facebook, and join our group to receive notifications of special events. If you find yourself in need of assistance, don't forget the Official Scripting Guys Forum. We have members from all around the world, so there is a good chance someone would be awake and willing to lend a hand. Until tomorrow, take care.

Ed Wilson and Craig Liebendorfer, Scripting Guys

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • All of the above is cool but can't work if you want you Write-Host or Write-Debug to be redirected to file as well as printing to console.

    Maybe the Scripting Guy can explain why (AFAIK these cmdlets are already redirecting their output).

    so these examples don't work!:

    Write-Debug "blablabla" > File.txt

    Write-Debug "blablabla" | Out-File -filepath C:\File.txt

    Write-Debug "blablabla" | Tee -filepath C:\File.txt

    so here's the function I finally use:

    Function WriteToDebug ([string]$DebugMessage)

    {

    $ScriptOut = ((Get-Date -format G )  + "`t$DebugMessage" )

    Out-File -FilePath $LogFileDebug -Append -InputObject $ScriptOut

    Write-Debug "$ScriptOut"

    }

    Where i replaced every Write-Debug in the script to WriteToDebug.