This week's Friday Fun actually has a purpose, at least for me. But I always hope you'll pick up a tip or two that you can use in your own PowerShell work.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
Because I write a lot about PowerShell, I am constantly copying pasting between my PowerShell session and usually Microsoft Word. Although the same is true when I am writing help examples for my functions. I can save command output to the clipboard with this trick:
Get-service | Group Status | clip
But I never know if the command was successful until I paste and then I need to copy the command again. Over the years I've come up with a number of tools to make this process easier but now I think I finally have it.
My new command is called Out-Copy. It is designed to accept any PowerShell command. The function will essentially pass the results back to the pipeline, so it is like Tee-Object in that respect. It also sends a copy of the output to the clipboard. But wait there's more! The clipboard output will include the prompt and the command you ran.
At a PowerShell prompt I can run something like this:
Using Out-Copy (Image Credit: Jeff Hicks)
And I can then paste into another application
Pasted results (Image Credit: Jeff Hicks)
I also included a parameter to only copy the command to the clipboard. This command runs as expected:
Copying command only (Image Credit: Jeff Hicks)
With this result:
Command only result (Image Credit: Jeff Hicks)
This version of my function will only copy what is sent to the success pipeline. It will not capture anything from the other streams such as Verbose, Warning or Error. Normally I do screen shots of that anyway. But this might be something I'll look into later.
Here's the complete function which includes an alias.
#requires -version 4.0 Function Out-Copy { <# .Synopsis Send command output to the pipeline and Windows clipboard. .Description This command is intended for writers and those who need to document with PowerShell. You can pipe any command to this function and you will get the regular output in your PowerShell session. But a copy of the output will be copied to the Windows clipboard. Additionally, the copy will include your prompt and the command that you executed, without the Out-Copy portion. NOTE: You can only capture what is written to the Success pipeline. This command will not copy any other streams such as Verbose or Error. .Parameter CommandOnly Only copy the executed command, without references to Out-Copy, to the Windows clipboard. .Parameter Width Specifies the number of characters in each line of output. Any additional characters are truncated, not wrapped. .Example PS C:\> Get-Process | Sort WS -Descending | Select -first 5 | out-copy This will execute your expression and write the output to the pipeline. In addition this text will be copied to the Windows clipboard: PS C:\> Get-Process | Sort WS -Descending | Select -first 5 Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName ------- ------ ----- ----- ----- ------ -- ----------- 1242 133 1047776 1002596 1987 4,499.78 1416 waterfox 1182 101 438800 409020 1285 854.53 6528 powershell_ise 321 22 245568 202236 362 730.05 2484 ZeroConfigService 1314 177 229772 187324 792 ...70.95 6500 SugarSync 651 37 199160 162216 343 597.16 1052 svchost .Example PS C:\scripts> dir *.ps1 | out-file c:\work\ps.txt Even if your command doesn't write anything to the pipeline, Out-Copy will still capture your prompt and PowerShell expression. .Example PS C:\> gcim win32_logicaldisk -filter "drivetype = 3" | out-copy -commandonly This will run the Get-CimInstance command and write results to the pipeline. But the only text that will be copied to the clipboard is: gcim win32_logicaldisk -filter "drivetype = 3" .Notes Last Updated: 30 October 2015 Version : 1.0 Learn more about PowerShell:Essential PowerShell Learning Resources**************************************************************** * DO NOT USE IN A PRODUCTION ENVIRONMENT UNTIL YOU HAVE TESTED * * THOROUGHLY IN A LAB ENVIRONMENT. USE AT YOUR OWN RISK. IF * * YOU DO NOT UNDERSTAND WHAT THIS SCRIPT DOES OR HOW IT WORKS, * * DO NOT USE IT OUTSIDE OF A SECURE, TEST SETTING. * **************************************************************** .Link Out-String .Inputs [object] .Outputs [object] #> [cmdletbinding()] Param( [Parameter(Mandatory,ValueFromPipeline)] [object]$InputObject, [ValidateNotNullorEmpty()] [int]$Width = 80, [switch]$CommandOnly ) Begin { Write-Verbose "Starting: $($MyInvocation.Mycommand)" Write-Verbose "Adding necessary .NET assembly" Add-Type -AssemblyName system.windows.forms #initialize an array to hold all incoming data $Data = @() } #begin Process { #add each input to the array $data += $InputObject } #process End { Write-Verbose "In the End block" #write data to the pipeline Write-Verbose "Here is the PowerShell output" $data Write-Verbose "Getting the currently running command" $Invoked = $MyInvocation.Line #parse out the Out-Copy command $cmd = $Invoked.substring(0,$invoked.LastIndexOf("|")) if ($CommandOnly) { Write-Verbose "Copying command expression" $Text = $cmd } else { #convert data to text Write-Verbose "Getting current prompt" $text = "$((Prompt | Out-String).Trim()) " $text += $cmd #insert a blank line $text += "`n" Write-Verbose "Converting data to text" #using a regular expression to try and clean up the output $text += ($data | Out-String -Width $Width) -replace "(?<=\S*)\s+`r`n$","`r`n" } Write-Verbose "Copy text to the clipboard" [System.Windows.Forms.Clipboard]::SetText( $text,[System.Windows.Forms.TextDataFormat]::Text) Write-Verbose "Ending: $($MyInvocation.Mycommand)" } #end } #define an alias Set-Alias -Name oc -Value Out-Copy
The only other parameter is Width. I have given this a default value of 80 which works best when pasting into a Word or text document. You can adjust this, typically up to the width of your PowerShell host. Anything beyond will be truncated.
I spent a few hours polishing this up, but it will save me time and frustration from here on out and maybe you will need to use it to. Plus it puts a smile on my face when I use it because I feel like I'm performing magic.
Enjoy!
Out-Copy is a very handy function. Thanks!
Thanks for the feedback. How do you think you’ll use it?
Is there a reason you delay writing the data back to the pipeline to the end block, rather than writing it directly back to the pipeline in the process block?
I know the intent of your code is to only run at the end of a pipeline, in which case you aren’t delaying it by much. That said, I could see a corner case for running Out-Copy in the middle of a pipeline. In that case, you are delaying any further processing of the objects until your command completes. Correcting that issue is a simple matter of adding a “Write-Output $InputObject” line in the process block and removing your “$Data” line from the end block.
That use case would also prevent your simple trim for capturing the command line. I would end up getting the aliases and examine the line for the command or its aliases and trimming it out, but you could end up with issues with concurrency if multiple Out-Copy commands are run at the same time. I will try playing with it and see if I can come up with a workable solution.
I initially was passing output in the process block. But the result was a single formatted object. So that instead of getting a result like you would see with Get-Service I was getting this:
Status Name DisplayName
—— —- ———–
Stopped AdobeFlashPlaye… Adobe Flash Player Update Service
Status Name DisplayName
—— —- ———–
Stopped AeLookupSvc Application Experience
Status Name DisplayName
—— —- ———–
Running BITS Background Intelligent Transfer Ser…
But I’m open to revisiting this.
This works well with this clipboard archive utility by Boe Prox at
https://gallery.technet.microsoft.com/scriptcenter/PowerShell-Clipboard-c414ec78
also written in PowerShell.