Stuffing the output of the last command into an automatic variable

UPDATE:

/\/\o\/\/ and Joel Bennet both chimed in and provided a much more elegant solution, just override out-default. This is really what I had wanted to do, but didn’t know how. Thanks to both /\/\o\/\/ and Joel for your input. Please do check out their comments on this post. But here is the code they provided:

   1: # From /\/\o\/\/
   2:  
   3: function out-default {
   4:     $input | Tee-Object -var global:lastobject | 
   5:     Microsoft.PowerShell.Utility\out-default
   6: }
   7:  
   8: # And from Joel 
   9: # In case you are using custom formatting
  10: # You will need to override the format-* cmdlets and then
  11: # add this to your prompt function
  12:  
  13: if($LastFormat){$LastOut=$LastFormat; $LastFormat=$Null }
  14:  
  15:  
  16:  

A couple of days ago, an intern that is working for us, was helping me with a Powershell script to manage our Hyper V Cluster. The script ran fine but we were querying 7 different computers and then rolling up all the output into a custom object, so the thing took a while to run. It was just long enough to be annoying. During this process, he asked if there was a way to have PowerShell automatically store the output of the last command in a variable automatically.

I first went down the road of using Tee-object,  According to the built-in help,

The Tee-Object cmdlet send the output of a command in two directions (like the letter T). It stores the output in a file or variable, and also sends it down the pipeline. If Tee-Object is the last command in the pipeline, the command output is displayed in the console.

Here’s Tee-Object in action

PS C:\> get-process notepad | tee-object -variable note

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     50       2     1264       6596    60     0.06   6984 notepad


PS C:\> $note

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     50       2     1264       6596    60     0.06   6984 notepad


PS C:\>

But of course, my Intern (rightfully so) declares this to be unsatisfactory. He wants it to just happen automagically. Picky intern, huh?

Then on the bus ride home this afternoon, I was thinking about Jeffrey’s post on Push-Noun. He basically shows us how to set up a loop that goes forever, taking input and executing it only for a specific noun in Powershell.

Considering this, I realized I could do something similar for my issue. So here’s the code, stolen from Push-Noun.

function Set-LastObjectAvailable {
while ($TRUE) 
{ 
    Write-Host "[LASTOBJECT]> " -NoNewLine 
    $line = $Host.UI.ReadLine().trim() 
    switch ($line) 
    { 
    "exit"   {return} 
    "quit"   {return} 
    "?"      {"Just type a command and the output will be displayed and stored in `$lastobject" } 
    {$_.StartsWith("!")} 
             { 
                $Cmd = $_.SubString(1) 
                Invoke-Expression $line |Tee-Object -varialbe lastobject | Out-Host
             } 
    default  { 
                
                Invoke-Expression $line |Tee-Object -Variable lastobject | out-host
             } 
    } 
}
}

And here it is in action:

PS C:\> Set-LastObjectAvailable
[LASTOBJECT]> get-process notepad

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     50       2     1264       6600    60            6984 notepad
     52       2     1256       4412    58     0.06  10152 notepad


[LASTOBJECT]> $lastobject

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     50       2     1264       6600    60            6984 notepad
     52       2     1256       4412    58     0.06  10152 notepad


[LASTOBJECT]> ?
Just type a command and the output will be displayed and stored in $lastobject
[LASTOBJECT]> gps notepad

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     50       2     1264       6600    60            6984 notepad
     52       2     1256       4412    58     0.06  10152 notepad


[LASTOBJECT]> $lastobject.count
2
[LASTOBJECT]> exit
PS C:\>

This isn’t exactly bulletproof. I think I would like to have an automatic variable that stores the output of the last command that was executed, just in case you want to mess with it and you don’t want to have to run it again. Basically just override out-host to use a Tee-Object -variable lastcommandoutput or something along those lines.

Advertisements

4 Responses to “Stuffing the output of the last command into an automatic variable”

  1. /\/\o\/\/ Says:

    Another “trick” to do this is to “override” out-default by creating a function by the same name like this :

    function out-default {$input | Tee-Object -var global:lastobject | Microsoft.PowerShell.Utility\out-default}

    that’s all needed, put this line in your profile to have this functionality in every PowerShell session.

    Enjoy,
    Greetings /\/\o\/\/

  2. Andy Says:

    Thanks /\/\o\/\/ !!

    That’s great. I forgot functions override cmdlets.

    Andy

  3. Joel "Jaykul" Bennett Says:

    I came over here to suggest … what mow said … so I’ll just mention the one problem he didn’t mention: when you override out-default like that … if you use a custom FORMAT-* cmdlet, the output stored in your $LastObject variable will be the format objects.

    Of course, you can overrides each of the format cmdlets that you use with functions (or script cmdlets) too, but then you *MUST* use a different variable (eg: have out-default store the data in $LastOut, and have Format-* store the data in $LastFormat) … if it bugs you to have them in different places, you can put something like this in your prompt function:

    if($LastFormat){$LastOut=$LastFormat; $LastFormat=$Null }

  4. Arnoud Jansveld Says:

    I really like /\/\o\/\/ and Joel’s solution, but it has one drawback: it collects all output until the pipeline is done before displaying them on screen. I wonder if there is another way to override Out-Default but pass the objects as they come in…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: