As someone who writes a lot of PowerShell scripts, there are certain cmdlets and scripting techniques that I find myself using quite often. Last week it occurred to me that I had never written about one PowerShell cmdlet in particular that can make it a little bit easier to write complex scripts. I am talking about the Tee-Object cmdlet.
Before I tell you what the Tee-Object cmdlet does, I need to take a moment and talk about the “normal” PowerShell way of doing things. Typically, when you use a Get cmdlet (Get-VM, Get-Service, Get-Process, etc.) you will end up doing one of two things. One option is to send the output down the pipeline, and then either display the output on the screen or write it to a file. The other option is to write the cmdlet’s output to a variable. But what if you need to do both?
There are quite a few different PowerShell techniques for outputting information to multiple locations. Let’s pretend for a moment that for whatever reason I want to find out if the BitLocker service is running, and that I want to display the service’s status on screen, but also write it to a variable. One way of accomplishing this task would be to do something like this:
$A = Get-Service BDESVC $A
The first line of code captures the Get-Service cmdlet’s output (as it related to the BDESVC service) and assigns it to a variable named A$. The second line of code displays the contents of the variable to the screen. Hence, these two lines of code meet the stated objectives by displaying the command’s output on screen, while also writing it to a variable.
Now, let’s take a look at how the same thing might be done using the Tee-Object cmdlet. The cmdlet allows output to be written to the screen and simultaneously written to either a file or a variable. Here is an example of the command:
Get-Service BDESVC | Tee-Object -Variable A
You can see what the command’s output looks like in the figure below. The screen capture also shows what happens when I call the variable.
Another neat trick
OK, so that’s a neat trick, but there is more going on here than meets the eye. The Tee-Object cmdlet can be inserted in the middle of the command pipeline. In other words, my Tee-Object variable declaration does not have to be the end of the command. I can pipe the onscreen output to additional cmdlets just as I could if the Tee-Object command did not exist. Here is an example:
Get-Service BDESVC | Tee-Object -Variable A | Select-Object Name, Status
You can see what this command does in the figure below. You can also see that the variable’s contents are not an exact match for what is being displayed onscreen, because I declared the variable before I used the Select-Object cmdlet.
Even though I tend to use the Tee-Object cmdlet mostly for the purpose of defining variables from within the command pipeline, there are other uses. Just as you can use the Tee-Object cmdlet to define a variable, you can also use it to write a command’s output to a file.
The syntax used for doing this is nearly identical to what you have already seen. All you have to do is to replace -Variable with -Filepath. You would of course, also replace the variable name with the file path and file name that you want to use. In most cases, the path and filename will need to be enclosed in quotation marks.
To show you how this works, let’s suppose that you wanted to take a look at the Bitlocker service, just as we did before. This time though, we want to dump the command’s output to a file instead of writing it to a variable. The command for doing so would look something like this:
Get-Service BDESVC | Tee-Object -FilePath “C:\Data\Bitlocker.txt”
Just as we can use the Tee-Object command to declare a variable from the middle of the pipeline, we can also write a file from the middle of the pipeline. Here is one of my earlier examples, but modified so that it produces a file rather than a variable.
Get-Service BDESVC | Tee-Object -FilePath “C:\Data\Bitlocker.txt” | Select-Object Name, Status
Two for the price of one cmdlet
One of the really cool things that you can do with the Tee-Object command is that you can use it to write two files simultaneously. Let’s suppose for a moment that you have a PowerShell script that produces logging data. Let’s also pretend that while the latest logging data is of the most interest to you, the older logging data also has value. By using the Tee-Object cmdlet, we could create two different log files. One of these logs can contain the latest logging data, while the other contains cumulative logging data. Going back to my Bitlocker Service example, here is how the command could be structured:
Get-Service BDESVC | Tee-Object -FilePath “C:\Data\CumulativeLog.txt” -Append | Out-File C:\Data\NewLog.txt
As you can see, I am using the FilePath cmdlet to create the cumulative log, and the Out-File cmdlet to create the new log file. In reality, I could have used either command to create either type of log file. The reason why the Tee-Object is creating a cumulative log file, in this case, is because of the -Append parameter. This parameter tells PowerShell to append the data to the existing file, rather than simply overwriting the existing log file with a new file.
Tee-Object: A great time saver
The Tee-Object cmdlet has a lot of potential to make PowerShell scripting less complicated. I recently wrote an unrelated article for this site on how to use PowerShell to figure out which Hyper-V server was hosting a particular VM. I made use of the Tee-Object cmdlet within that particular script because it allowed me to avoid some tricky scripting.