如何在Powershell中将输出捕获到外部进程的variables中?

我想运行一个外部进程,并将其命令输出捕获到Powershell中的variables。 我目前正在使用这个:

$params = "/verify $pc /domain:hosp.uhhg.org" start-process "netdom.exe" $params -WindowStyle Hidden -Wait 

我已经确认该命令正在执行,但我需要捕获输出到一个variables。 这意味着我不能使用-RedirectOutput,因为这只会redirect到一个文件。

你有没有尝试过:

$OutputVariable = (Shell command) | Out-String

从根本上说 ,捕获外部实用程序的输出与PowerShell本地命令相同 (您可能需要回顾一下如何执行外部工具 ):

 $cmdOutput = <command> # captures the command's success stream / stdout 

捕获variables中的输出打印到屏幕上

 <command> | Tee-Object -Variable cmdOutput # Note how the var. name is NOT $-prefixed 

要捕获多个命令的输出,请使用子expression式( $(...) )或调用( &. )脚本块( { ... } ):

 $cmdOutput = $(<command>; ...) # subexpression $cmdOutput = & {<command>; ...} # or: script block with &; creates child scope for vars. $cmdOutput = . {<command>; ...} # or: script block with .; no child scope 

请注意, 通常需要以& (调用操作符)作为前缀,其名称/path被引用的单个命令 (例如, $cmdOutput = & 'netdom.exe' ... )与外部程序本身无关适用于PowerShell脚本),但是是语法要求 :PowerShell默认情况下会parsing以expression式模式开头的带引号string的语句 ,而需要使用参数模式

redirect也基本相同(但请参阅下面的注意事项):

 $cmdOutput = <command> 2>&1 # redirect error stream (2) to success stream (1) 

但是,对于外部命令,以下更可能按预期方式工作:

 $cmdOutput = cmd /c <command> '2>&1' # Let cmd.exe handle redirection - see below. 

特定于外部程序的注意事项:

  • 外部程序 ,因为它们在PowerShell的types系统之外运行,所以只能通过成功stream(stdout) 返回string

  • 如果输出包含多于一行的话 ,默认情况下,PowerShell将它分割成一个string数组 。 更准确地说,输出行存储在一个[System.Object[]]types的数组中,其元素是string( [System.String] )。

  • 如果您希望输出为单个可能为多行的string则将其输出 Out-String
    $cmdOutput = <command> | Out-String

  • 将stderrredirect到2>&1标准输出 ,以便也将其作为成功stream的一部分进行捕获,并提出以下注意事项

    • 要使源代码中的 2>&1合并标准输出和标准错误cmd.exe处理redirect ,使用以下成语:
      $cmdOutput = cmd /c <command> '2>&1' # *array* of strings (typically)
      $cmdOutput = cmd /c <command> '2>&1' | Out-String # single string

      • cmd /c用命令<command>调用cmd.exe ,并在<command>完成后退出。
      • 请注意2>&1附近的单引号,这可确保将redirect传递给cmd.exe而不是由PowerShell解释。
      • 请注意,涉及cmd.exe意味着的转义字符和扩展环境variables的规则在默认情况下除了PowerShell自己的要求之外, 在PS v3 +中,除了cmd.exe风格的环境variables引用(如%PATH%之外,可以使用特殊参数--% (所谓停止parsing符号 )closuresPowerShell对其余参数的解释。

      • 请注意,由于您正在使用此方法在源代码中合并stdout和stderr ,因此 您将无法区分 PowerShell中由stdout起源和由stderr起源的行 ; 如果您确实需要这种区别,请使用PowerShell自己的2>&1redirect – 请参见下文。

    • 使用PowerShell的 2>&1redirect来知道哪些行来自哪个stream

      • Stderr输出被捕获为错误logging[System.Management.Automation.ErrorRecord] ),而不是string,所以输出数组可能包含混合 string (每个string代表一个stdout行)和错误logging (每个logging代表一个stderr行) 。 请注意,按照2>&1要求,通过PowerShell的成功输出stream接收string和错误logging。

      • 在控制台中,错误logging以红色打印,默认情况下, 一个产生多行显示,格式与cmdlet的非终止错误显示的格式相同; 随后的错误logging也以红色打印,但只在一行中打印错误消息

      • 当输出到控制台时 ,string通常在输出数组中首先出现,然后是错误logging(至less在一批stdout / stderr行输出“在同一时间”),但幸运的是, 当您捕获输出,它是正确的交错 ,使用相同的输出顺序,你会得到2>&1 ; 换句话说: 当输出到控制台时 ,捕获的输出不会反映外部命令生成stdout和stderr行的顺序。

      • 如果使用Out-String单个string中捕获整个输出PowerShell将添加额外的行 ,因为错误logging的string表示包含额外的信息,如位置( At line:... )和类别( + CategoryInfo ... ); 好奇地,这只适用于第一个错误logging。

        • 要解决此问题,请将.ToString()方法应用于每个输出对象,而不是pipe道到Out-String
          $cmdOutput = <command> 2>&1 | % { $_.ToString() } $cmdOutput = <command> 2>&1 | % { $_.ToString() } ;
          在PS v3 +中,您可以简化为:
          $cmdOutput = <command> 2>&1 | % ToString
          (作为奖励,如果输出未被捕获,则即使在打印到控制台时也会产生正确的交错输出。)
      • 或者, 过滤错误logging,并将它们发送到PowerShell的错误stream与Write-Error (作为奖金,如果没有捕获输出,这将产生正确的交错输出,即使打印到控制台):

 $cmdOutput = <command> 2>&1 | ForEach-Object { if ($_ -is [System.Management.Automation.ErrorRecord]) { Write-Error $_ } else { $_ } } 

如果你想redirect错误输出,你必须这样做:

 $cmdOutput = command 2>&1 

或者,如果程序名称中有空格:

 $cmdOutput = & "command with spaces" 2>&1 

或者试试这个。 它将捕获输出到variables$ scriptOutput中:

 & "netdom.exe" $params | Tee-Object -Variable scriptOutput | Out-Null $scriptOutput 

另一个真实的例子:

 $result = & "$env:cust_tls_store\Tools\WDK\x64\devcon.exe" enable $strHwid 2>&1 | Out-String 

注意这个例子包含一个path(以一个环境variables开始)。 注意引号必须围绕path和exe ,而不是参数!

注意:不要忘记命令前面的&字符,但在引号之外。

错误输出也被收集。

我花了一段时间才得到这个组合的工作,所以我虽然我会分享它。

我试了答案,但在我的情况下,我没有得到原始的输出。 相反,它被转换为Powershellexception。

我得到的原始结果:

 $rawOutput = (cmd /c <command> 2`>`&1) 

如果你所要做的只是从一个命令中捕获输出,那么这将工作的很好。 我用它来改变系统时间,因为[timezoneinfo]::local总是产生相同的信息,即使你对系统进行了改变之后 。 这是我可以validation和logging时区变化的唯一方法:

 $NewTime = (powershell.exe -command [timezoneinfo]::local) $NewTime | Tee-Object -FilePath $strLFpath\$strLFName -Append 

这意味着我必须打开一个新的PowerShell会话来重新加载系统variables

我试图在PowerShell脚本中执行git命令(并将输出捕获到variables中),使用此线程中的信息,我可以非常方便地执行命令,只需执行以下操作:

 # Make sure we're in the correct folder cd myGitRepo # Fetch sha1 of a specific commit into a Powershell variable - works fine without variable assignment git log --grep='My specific commit message here' --format=%H # This does not work, git reports 'not a git repository' $myVar = git log.... # git fatal: not a git repo 

似乎variables赋值function忽略了当前path,并使命令在根文件夹(不是git repo)中执行。 尼永知道为什么会发生这种情况,我怎么能解决这个问题?

更新2 :cd是Set-Location的别名,所以我必须得到一些完全错误的东西。

更新 :奇怪的是,做设置位置(而不是光盘) myGitRepo似乎解决了这个问题,即这个工程:

 Set-Location '.\myGitRepo' $myVar = git log .....