54

我正在寻找可与 Python 交互式 shell 的“_”变量相媲美的功能。在 PowerShell 我想要这样的东西:

> Get-Something # this returns an object and display it to the output.
# Now I want to assign that object to some variable
> $anObj = ???
4

11 回答 11

25

不,没有这样的自动变量。

你所要做的:

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

获得行为

于 2013-01-16T04:02:16.187 回答
17

如前所述,没有对此的内置支持,但这是一个简单但次优的 PSv3+自定义解决方案

笔记:


将以下内容添加到您的$PROFILE文件中:

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

命名变量的名称$__(例如本例中的(2 个下划线))由您决定,但请注意名称冲突,尤其是与$_在许多上下文中表示手头输入对象的自动变量的名称冲突。

这将$__通过 PowerShell 全局预设参数默认值的能力来捕获交互式会话中最近执行的PowerShell命令 [产生终端输出]的终端绑定Get-Help about_Parameters_Default_Values输出 - 请参阅.

-OutVariable是一个通用参数,旨在将 cmdlet/高级函数的输出对象收集到变量中,并且上述定义将此参数隐式应用于所有Out-Default调用,每当 PowerShell 向终端输出某些内容时,都会在后台调用该参数 - 但是,请注意下面提到的例外情况。

注意事项

  • 如果需要,请使用$saved = $__.Clone()保存捕获的输出以供以后使用,因为它$__会在每个命令上重新分配(当然,如果您提前知道要保留命令的输出,请使用以开头的分配:)$saved = <command>

    • 请注意,just$saved = $__不起作用,因为$saved指向与 相同的[ArrayList]实例$__,该实例将在下一个命令中重新填充。
  • 在以下情况下不会捕获输出:

    • 来自外部程序的输出,例如git,因为根据设计 PowerShell 将来自外部程序的输出流直接传递到终端(除非它们被重定向或捕获),因此不会调用Out-Default. 最简单的解决方法是通过管道传输Write-Output(类似于*>&1通过 PowerShell 流显式路由的方法不起作用);例如:

      • whoami.exe | Write-Output # $__ is now populated
    • 显式调用格式化 cmdlet的命令的输出- Format-CustomFormat-HexFormat-ListFormat-TableFormat-Wide.

      • 尝试用 来解决这个问题很诱人$PSDefaultParameterValues['Format-*:OutVariable'] = '__',但不幸的是,这会收集格式化对象(指令)而不是 中的原始数据$__这是不希望的。一个不令人满意的解决方法是在不同的变量中捕获Format-*输出,这不仅需要您考虑需要定位哪个变量,而且您仍然只能看到格式化对象而不是数据,而且由于cmdlet 甚至在幕后都涉及如果您没有明确使用它们,那么没有调用的命令的输出将被捕获两次- 一次作为数据, in ,另一次作为格式化对象,在另一个变量中。Format-* Format-*$__
  • 由于设计怪癖$__ 总是包含一个数组列表(类型[System.Collections.ArrayList]),即使前面的命令只输出一个对象。如有疑问,请使用$($__)(或$__[0]) 来获取单个输出对象。

  • 注意产生非常大的输出集的命令,因为$__会将它们收集在内存中

  • $__只会捕获输出到终端的对象- 就像_在 Python 中一样;一个不产生输出的命令或$null/ 一个 s 的数组会保留$null任何先前的$__值不变。

于 2018-09-24T18:25:57.363 回答
6

您还可以使用 OutVariable 参数打印命令的结果并捕获输出对象,然后使用 $anObj 显示变量内容。

Get-Something -OutVariable anObj
于 2013-01-16T06:45:35.013 回答
6

最后一个需要大部分工作的选项,但 IMO 为您提供了您所要求的:创建将覆盖 Out-Default 的代理(如果您不使用其他内容,则始终在管道末尾隐式调用该代理)。

Jeffrey Snover 在一次 PowerShell Deep Dives(我认为这是第一次)中对它进行了介绍 - 你可以在Dmitry Sotnikov 博客上找到他使用的脚本(包括上面提到的默认) 。您还可以观看其中的视频以了解整个概念。

于 2013-01-16T08:26:15.223 回答
3

不完全是。有 $_ 自动值,其中包含管道中的当前对象。

管道是将结果从一个 cmdlet 传递到下一个的常用方法,并且 cmdlet 被设置为接受来自管道的参数或管道中对象的属性,从而使“最后结果”变量的使用无关紧要。

仍然有些情况确实需要对“管道”对象的特定引用,对于那些有 $_ 自动值。

下面是它的用法示例:使用 Where-Object Cmdlet,这里是 powershell 的自动变量列表:第 4 章。PowerShell 自动变量

powershell 中的脚本编写需要与 Python 中的编程不同的风格(就像 Python 需要与 C++ 不同的风格一样。)

Powershell 的构建使得管道被广泛使用,如果您想将管道分解为更程序化的逐步结构,您需要将结果保存在命名变量中,而不是自动变量中。

于 2013-01-16T04:00:40.713 回答
3

用“r”(Invoke-History 的别名)调用最后一个命令并将其包裹在括号()中以首先执行它怎么样?

是的,这会重新运行最后一个命令,但在我的用例中,这是迄今为止最精简的解决方案,尤其是当我没有意识到我首先需要最后一个命令输出的时候。

它还保持对象结构完整。

PS C:\Users\user> Get-NetAdapter -InterfaceIndex 3 | Where {$_.State -match "2"}

Name                      InterfaceDescription                    ifIndex Status       MacAddress             LinkSpeed
----                      --------------------                    ------- ------       ----------             ---------
Ethernet                  Intel(R) Ethernet Connection I217-LM          3 Up           XX-XX-XX-XX-XX-XX       100 Mbps

PS C:\Users\user> (r) |where {$_.LinkSpeed -eq "100 Mbps"}
Get-NetAdapter -InterfaceIndex 3 | Where {$_.State -match "2"}

Name                      InterfaceDescription                    ifIndex Status       MacAddress             LinkSpeed
----                      --------------------                    ------- ------       ----------             ---------
Ethernet                  Intel(R) Ethernet Connection I217-LM          3 Up           XX-XX-XX-XX-XX-XX       100 Mbps

PS C:\Users\user> (r).MacAddress
Get-NetAdapter -InterfaceIndex 3 | Where {$_.State -match "2"}
XX-XX-XX-XX-XX-XX
于 2016-11-07T16:47:54.707 回答
1

对于我的特定用例,我从 PowerShell 运行批处理文件,我想实时打印该批处理文件的输出并将输出保存到变量中。我可以通过将呼叫操作员的输出传送到Tee-Object

$args = @('-r', '-a');
& "C:\myFile.bat" $args | Tee-Object -Variable output;
$output | Set-Clipboard;

第一个命令为批处理文件设置我的参数。第二个命令使用带有我的参数的调用运算符运行批处理文件,并将输出通过管道传递给Tee-Object命令,该命令实时打印调用运算符的输出,但还将所有信息保存到一个名为output的新变量中。最后一个命令只是将 的内容复制$output到剪贴板。

Tee-Object还允许将输出保存到文件(Unicode 编码),如果我需要保存到文件变量(除了打印到控制台),我可以将多个调用链接到Tee-Object一个管道中。有关更多信息,请参阅此链接:

https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/tee-object

于 2018-08-08T15:26:50.563 回答
1

这是来自 Windows Powershell in Action。在你的 $profile 中获取它,它使 $last 包含最后一个命令的输出。它还将通过在 la zsh 中键入目录名称来更改目录,或者通过键入网站的 url 在浏览器中访问网站。它创建了一个 Out-Default 函数,该函数覆盖 Out-Default cmdlet 并调用它。我添加了“start-process $__command”行,所以去一个网站可以在 osx 中工作。

这是相当具有侵略性的。后来我出于某种原因在我的 $profile 中禁用了它。

#
# 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()
  }

}
于 2019-08-11T14:54:34.973 回答
0

一种对我有用的方法是将命令的输出Select-Object作为传递来处理。例如,我有一个控制台 EXE,它输出详细输出到stderr和有意义的输出到stdout,我只想捕获最后一行。

$UtilityPath = ""

(UpdateUtility.exe $ArgList 2>&1) | % {
    If ($_ -Is [System.Management.Automation.ErrorRecord]) {
        $_.Exception.Message
    }
    Else {
        $_
        $UtilityPath = $_
    }
}

调用此脚本的方式,在 PowerShell 的Error输出流上输出被认为是一个硬错误,这与 PowerShell 获取外部应用程序的stderr输出并将其转换为Error输出的方式不符。这种包装输出的方法使我能够控制它如何通过,以及捕获stdout我想要的行。在我看来,这将是一种相当灵活的方法,可以让您拦截输出并在输出通过时对其执行任何您想做的事情。例如:

$CommandOutput = ""

SomeOtherCommand | % {
    $CommandOutput += "$_`r`n"
    $_
}
于 2019-01-28T21:00:41.783 回答
-1

使用模块 PowerShellCookbook 并将对 Add-ObjectCollector 的调用添加到您的启动脚本

于 2016-09-14T13:43:29.830 回答
-2

您可以尝试“ (Get-History)[-1].CommandLine ”。

于 2021-10-04T11:07:40.073 回答