如何在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>&1
redirect – 请参见下文。
-
-
使用PowerShell的
2>&1
redirect来知道哪些行来自哪个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 .....