Teeing Up to the Clipboard

Because I spend a great part of my day creating PowerShell related content, I often need to copy command output from a PowerShell session. The quick and dirty solution is to pipe my expression to the Clip.exe command line utility.

This works in both the console and PowerShell ISE. But there are situations where I’d like to see the result so that I know it is worth pasting into whatever I’m working on. In other words I want to send the results of the command to the pipeline and the clipboard at the same time. PowerShell can already do something similar with the Tee-Object cmdlet. With that cmdlet you can tee output to a variable or a file. So I decided to build a wrapper around Tee-Object and add functionality to send tee output to the clipboard.

I created a copy of Tee-Object and started modifying. I didn’t want to reverse-engineer the cmdlet. I simply wanted to add a new parameter. In some ways, my new command is like a proxy function.

I use Tee-MyObject much the same way as Tee-Object, even with the same parameters. Those are splatted through $PSBoundParameters. There are a few items I want to point out, more as a matter of scripting technique than anything.

First, because my function is wrapping around Tee-Object, which typically has input piped to it, I needed to temporarily store piped in objects to my function. That’s why I initialize a variable, $data and add each input object to it. After everything has been piped to my function, I can pass the data to Tee-Object, assuming I’m using one of its parameters.

However, because I am running Tee-Object inside of a function, scope becomes an issue. When I use the –Variable parameter, Tee-Object creates the variable with no problem. But when the function ends, the variable is destroyed. My solution is to create a new copy of the variable and specify the parent scope.

I needed this “tricks” in order to pass on full functionality between my wrapper function and Tee-Object.

For the clipboard part, I originally used code to simply pipe data to Clip.exe. However, this can temporary flash a console window and depending on the command I also end up with lots of extra white space at the end of each line. So I decided to use the .NET framework to add content to the clipboard. The tricky part was converting the output to text and cleaning it up. I ended up using the Trim() method to remove leading and trailing spaces after piping the data to Out-String. This leaves me with one long string. To clean up the extra space at the end of some lines, I use the –Replace operator to find anything that matches a bunch of spaces followed by the new line marker and replace it with just the new line marker. This has the effect of trimming away all those spaces.

And that’s it! My version even has a modified copy of the help for Tee-Object.

Modified help

I can use my command in place of Tee-Object. I even defined my own alias.

Using Tee-MyObject

To be clear, I have not modified, overwritten or removed Tee-Object. I have simply created a new version of the command with some additional functionality. If you have any questions on my process or some scripting element, please post a comment.

Enjoy!

Have Your Output and Variable Too

There’s a relatively useful suggestion floating around on Twitter on how to save results of PowerShell command to a variable and see the results at the same time.


PS C:\> ($data=get-process)

I’ll admit this is a clever technique: you get the results from Get-Process written to the pipeline AND a variable $data. The other way, and frankly I think a better way, is to use Tee-Object. Usually we use Tee-Object to save output to a file and still write the results to the pipeline.


PS C:\> get-process | tee -variable data

To me, this is much more straightforward and I don’t have to remember a punctuation trick. I’ve also found that using Tee-Object is also better performing. I’ve tried a number of tests and they all seem to verify that Tee-Object is faster. Here’s one test:


PS C:\Users\Jeff> measure-command {($a=dir $env:temp -rec)}

Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 468
Ticks : 4686037
TotalDays : 5.42365393518518E-06
TotalHours : 0.000130167694444444
TotalMinutes : 0.00781006166666667
TotalSeconds : 0.4686037
TotalMilliseconds : 468.6037

PS C:\Users\Jeff> measure-command {dir $env:temp -rec | tee -Variable a}

Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 158
Ticks : 1583448
TotalDays : 1.83269444444444E-06
TotalHours : 4.39846666666667E-05
TotalMinutes : 0.00263908
TotalSeconds : 0.1583448
TotalMilliseconds : 158.3448

I get the same result with both commands but you can see that Tee-Object is significantly faster.

Using the parentheses trick is handy and especially useful in the interactive console. Personally, I’ll probably stick to using Tee-Object and certainly in a script using Tee-Object is easier to follow and understand.

Friday Fun: Output to 2 Places in 1

Today’s Friday Fun comes out of a short exchange I had yesterday with Hal Rottenberg on Google Plus. We were playing around with piping a PowerShell command to Clip.exe which dumps the output to the Windows Clipboard. I got to thinking about taking this a step further based on my needs as a writer. Often I’d like to see the results of a command and then copy and paste the results into whatever I’m working on. In other words, I need to TEE the output to two places.

PowerShell has a cmdlet called Tee-Object that follows this principal. The default behavior is to write output to the pipeline AND send it to a text file.


PS C:\> get-service | tee c:\work\svc.txt

I’ll see the results and save them to a text file. I can also use this cmdlet to save results to a variable.


PS C:\> get-service | tee c:\work\svc.txt

Status Name DisplayName
------ ---- -----------
Running AeLookupSvc Application Experience
Stopped ALG Application Layer Gateway Service
Stopped AppIDSvc Application Identity
Stopped Appinfo Application Information
...
Running wudfsvc Windows Driver Foundation - User-mo...
Stopped WwanSvc WWAN AutoConfig

PS C:\> $svc.count
196
PS C:\>

One approach I came up with to incorporate with Clip.exe was this:


PS C:\> get-service | tee -Variable svc | clip

I don’t get the results immediately to the screen; they are saved to the variable. But at the same time output has been directed to the Windows Clipboard. That could be useful. But you know me, I always have to tinker a bit more and I ended up with a function called Out-Tee.


Function Out-Tee {

[cmdletbinding()]

Param (
[Parameter(Position=0,Mandatory=$True,ValueFromPipeline=$True)]
[object[]]$InputObject,
[alias("foregroundcolor","fg")]
[string]$TextColor=$host.ui.rawui.ForegroundColor
)

Begin {
#define an empty array to hold piped in objects
[email protected]()
}

Process {
#add each piped in object to the array
$a+=$inputobject
}

End {
#write the array to the pipeline as a string then pass to Write-Host
$a | out-string | write-host -fore $textColor
#write the array again to Clip.exe
$a | clip
}

} #end function

This simple function takes a PowerShell expression and writes the results to the console using Write-Host and also to the clipboard. The default output will use the current console foreground color. But you can specify any other color that you would use with Write-Host. I even added some alias properties so you can use -foregroundcolor or -fg.

With this function I can see the result and have it dumped to the clipboard. Because the default text color is the same as my session, I don’t see any difference when using Out-Tee.


PS C:\> ps | where {$_.ws -gt 100mb} | out-tee

Or if I want to pretty it up, I can add a little color.


PS C:\> ps | where {$_.ws -gt 100mb} | out-tee -TextColor green

Of course, the clipboard is just text. But now I have something easier to use to save output to the clipboard so I can paste it into my documents, assuming I like the output I see on the screen. The one caveat is that this function only works with successful commands. Errors, warnings, or verbose statements won’t get dumped to the clipboard. I can think of some ways around that which I might try in a future version. But for my immediate needs this works just fine.

Download Out-Tee and give it a try.