PowerShell: Is there an automatic variable for the last execution result?

前端 未结 10 1872
名媛妹妹
名媛妹妹 2021-02-05 00:29

I\'m looking for a feature comparable to Python interactive shell\'s \"_\" variable. In PowerShell I want something like this:

> Get-Something # this returns          


        
相关标签:
10条回答
  • 2021-02-05 01:08

    No there is not automatic variable like that.

    You have to do:

    $output = Get-Something
    $output
    $anObj = $output
    

    to get the behaviour

    0 讨论(0)
  • 2021-02-05 01:08

    Last option that requires most work but IMO gives you what you ask for: create proxy that will overwrite Out-Default (that is always called implicitly at the end of pipeline, if you don't out-* to something else).

    Jeffrey Snover gave presentation on it during one of PowerShell Deep Dives (I reckon it was the first one) - you can find scripts he used (including above mentioned out-default) on Dmitry Sotnikov blog. You can also watch video from it to understand whole concept.

    0 讨论(0)
  • 2021-02-05 01:12

    As stated, there's no built-in support for this, but here's a simple, but suboptimal PSv3+ custom solution:

    Note:

    • For a proper, but nontrivial solution, see BartekB's helpful answer.

    • There is a feature request on Github to build this functionality into a future PowerShell Core version (the current version as of this writing is PowerShell [Core] 7.0).


    Add the following to your $PROFILE file:

    # Store previous command's output in $__
    $PSDefaultParameterValues['Out-Default:OutVariable'] = '__'
    

    What to name the variable - such as $__ (2 underscores) in this example - is up to you, but beware of name collisions, notably with $_, the automatic variable that represents the input object at hand in a number of contexts.

    This will capture the terminal-bound output of the most recently executed PowerShell command [that produced terminal output] in variable $__ in your interactive sessions, by way of PowerShell's ability to globally preset parameter defaults - see Get-Help about_Parameters_Default_Values.

    -OutVariable is a common parameter designed to collect a cmdlet's / advanced function's output objects in a variable, and the above definition applies this parameter implicitly to all Out-Default calls, which in turn is called behind the scenes whenever PowerShell outputs something to the terminal - however, note the exceptions mentioned below.

    Caveats:

    • If needed, use $saved = $($__) to save the captured output for later use, given that $__ is reassigned to on every command (of course, if you know ahead of time that you want to keep a command's output, use an assignment to begin with: $saved = <command>).

      • Note that just $saved = $__ does not work, because that makes $saved point to the same [ArrayList] instance as $__, which gets repopulated on the next command; $(...) (or @(...) if you also want a single-object result to be an array) ensures that the array list's elements are collected in a new array.
    • Output is not captured in the following cases:

      • Output from external programs, such as git, because by design PowerShell passes the output streams from external programs directly to the terminal (unless they're redirected or captured), and therefore doesn't call Out-Default. The simplest workaround is to pipe to Write-Output (something like *>&1 to explicitly route through PowerShell streams doesn't work); e.g.:

        • whoami.exe | Write-Output # $__ is now populated
      • Output from commands that explicitly call a formatting cmdlet - Format-Custom, Format-Hex, Format-List, Format-Table, or Format-Wide.

        • It's tempting to attempt to fix that with $PSDefaultParameterValues['Format-*:OutVariable'] = '__', but, unfortunately, this would collect formatting objects (instructions) rather than the original data in $__, which is undesired. An unsatisfying workaround is to capture Format-* output in a different variable, which not only requires you to think about which variable you need to target, but you'll still only see formatting objects rather than data, and, since Format-* cmdlets are involved behind the scenes even if you don't use them explicitly, the output of commands without Format-* calls is then captured twice - once as data, in $__, and again as formatting objects, in the other variable.
    • Due to a design quirk, $__ will always contain an array list (of type [System.Collections.ArrayList]), even if the previous command output only a single object. When in doubt, use $($__) (or $__[0]) to get a single output object as such.

    • Beware of commands producing very large output sets, because $__ will collect them in memory.

    • $__ will only capture objects output to the terminal - just like _ does in Python; a command that produces no output or $null / an array of $nulls leaves any previous $__ value intact.

    0 讨论(0)
  • 2021-02-05 01:20

    This is from Windows Powershell in Action. Source this in your $profile, and it makes $last contain the output of the last command. It also will change directories by typing the name of the directory a la zsh, or go to a website in a browser by typing the url of the website. It makes an Out-Default function that overrides the Out-Default cmdlet and calls it. I added the "start-process $__command" line so going to a website would work in osx.

    This is fairly invasive. I later disabled it in my $profile for some reason.

    #
    # Wrapping an exiting command with a function that uses
    # steppable pipelines to "remote-control" the wrapped command.
        
    function Out-Default
    {
      [CmdletBinding(ConfirmImpact="Medium")]
      param(
        [Parameter(ValueFromPipeline=$true)] 
        [System.Management.Automation.PSObject] $InputObject
      )
        
      begin {
        $wrappedCmdlet = $ExecutionContext.InvokeCommand.GetCmdlet(
          "Out-Default")
        $sb = { & $wrappedCmdlet @PSBoundParameters }
        $__sp = $sb.GetSteppablePipeline()
        $__sp.Begin($pscmdlet)
      }
    
      process {
        $do_process = $true
        if ($_ -is [System.Management.Automation.ErrorRecord]) {
          if ($_.Exception -is 
          [System.Management.Automation.CommandNotFoundException]) {
            $__command = $_.Exception.CommandName
            if (test-path -path $__command -pathtype container) {
              set-location $__command
              $do_process = $false
            }
            elseif ($__command -match '^http://|\.(com|org|net|edu)$') {
              if ($matches[0] -ne "http://") {$__command = "HTTP://" + 
                $__command }
              # [diagnostics.process]::Start($__command)
              start-process $__command
              $do_process = $false
            } 
          } 
        }
        if ($do_process) {
          $global:LAST = $_;
          $__sp.Process($_)
        }  
      }
    
      end {
        $__sp.End()
      }
    
    }
    
    0 讨论(0)
提交回复
热议问题