我想运行一个外部进程并将它的命令输出捕获到 PowerShell 中的变量。我目前正在使用这个:
$params = "/verify $pc /domain:hosp.uhhg.org"
start-process "netdom.exe" $params -WindowStyle Hidden -Wait
我已经确认命令正在执行,但我需要将输出捕获到一个变量中。这意味着我不能使用 -RedirectOutput 因为这只会重定向到一个文件。
最佳答案
注意:问题中的命令使用 Start-Process
,这会阻止直接捕获目标程序的输出。通常, do not use Start-Process
to execute console applications synchronously - just invoke them directly ,就像在任何 shell 中一样。这样做使应用程序连接到调用控制台的标准流,允许通过简单的赋值 $output = netdom ...
捕获其输出,如下详述。
从根本上说,从外部程序捕获输出的工作方式与 PowerShell 本地命令 相同(您可能需要复习 how to execute external programs ;<command>
是以下任何有效命令的占位符):
# IMPORTANT:
# <command> is a *placeholder* for any valid command; e.g.:
# $cmdOutput = Get-Date
# $cmdOutput = attrib.exe +R readonly.txt
$cmdOutput = <command> # captures the command's success stream / stdout output
请注意,如果 $cmdOutput
产生超过 1 个输出对象 ,则 <command>
接收一个对象数组,在 外部程序的情况下,这意味着包含程序输出行 _679204 的 string[1] 数组如果你想确保结果是 总是一个数组 - 即使只输出一个对象,将变量类型约束为一个数组,或者将命令包装在
@()
中,即 array-subexpression operator ):[array] $cmdOutput = <command> # or: $cmdOutput = @(<command>)
相比之下,如果您希望 $cmdOutput
始终接收 单行 - 可能是多行 - 字符串 ,请使用 Out-String
,但请注意 总是添加尾随换行符 _0x104567 讨论此问题的行为# Note: Adds a trailing newline.
$cmdOutput = <command> | Out-String
通过调用外部程序——根据定义,它只在 PowerShell[1] 中返回字符串——你可以通过使用 GitHub issue #14444 来避免这种情况:# NO trailing newline.
$cmdOutput = (<command>) -join "`n"
注意:为简单起见,上面使用 -join
来创建 Unix 风格的 LF-only 换行符,PowerShell 很乐意在所有平台上接受;如果您需要适合平台的换行符(Windows 上的 CRLF,Unix 上的 LF),请改用 "`n"
。在变量 中捕获 输出并打印到屏幕 :
<command> | Tee-Object -Variable cmdOutput # Note how the var name is NOT $-prefixed
或者,如果 [Environment]::NewLine
是 cmdlet 或高级函数,则可以使用 <command>
operator :<command> -OutVariable cmdOutput # cmdlets and advanced functions only
请注意,对于 -OutVariable
,与其他场景不同,-ov
始终是一个集合,即使只输出一个对象。具体来说,返回一个类似数组的 -OutVariable
类型的实例。有关此差异的讨论,请参阅 common parameter
$cmdOutput
/ [System.Collections.ArrayList]
。要捕获 多个命令 的输出,请使用子表达式 (
$(...)
) 或使用 { ... }
或 &
调用脚本块 (.
):$cmdOutput = $(<command>; ...) # subexpression
$cmdOutput = & {<command>; ...} # script block with & - creates child scope for vars.
$cmdOutput = . {<command>; ...} # script block with . - no child scope
请注意, 一般需要以 &
(调用运算符)为前缀的单个命令,其名称/路径被引用 - 例如,$cmdOutput = & 'netdom.exe' ...
- 与外部程序本身无关(它同样适用于 PowerShell 脚本),但是是 语法要求 :PowerShell 默认在表达式模式下解析以带引号的字符串开头的语句,而调用命令(cmdlet、外部程序、函数、别名)需要参数模式,这是 &
所确保的。$(...)
和 & { ... }
/. { ... }
的主要区别在于,前者将所有输入收集在内存中,然后作为一个整体返回,而后者将输出流式处理,适用于一对一的流水线处理。重定向 从根本上也是一样的(但请参阅下面的警告):
$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.
特定于外部程序的注意事项:
$OutVariable
首选项变量中的编码;在 Windows PowerShell 中默认为 ASCII(!),在 PowerShell [Core] 中默认为 UTF-8。[Console]::OutputEncoding
中的编码对数据进行解码,这在两个 PowerShell 版本中都默认为系统的事件 OEM 代码页。[System.Object[]]
类型的数组中,其元素是字符串 ( [System.String]
)。-join
运算符(您也可以通过管道连接到 Out-String
,但总是会添加一个尾随换行符):$cmdOutput = (<command>) -join [Environment]::NewLine
2>&1
合并到标准输出中,以便也将其捕获为成功流的一部分,附带 警告 _0x10456792cmd.exe
处理重定向 ,在 Unixtcode-like 平台上使用以下习语:sh
$cmdOutput = cmd /c <command> '2>&1' # *array* of strings (typically)
$cmdOutput = (cmd /c <command> '2>&1') -join "`r`n" # single string
使用命令 cmd /c
调用 cmd.exe
并在 <command>
完成后退出。<command>
周围的单引号,它确保重定向被传递给 2>&1
而不是被 PowerShell 解释。cmd.exe
意味着它的转义字符和扩展环境变量的规则起作用了,默认情况下,除了PowerShell本身的要求;在 PS v3+ 中,您可以使用特殊参数 cmd.exe
(所谓的停止解析符号)来关闭 PowerShell 对其余参数的解释,除了 --%
风格的环境变量引用,例如 cmd.exe
。%PATH%
重定向 - 见下文。2>&1
重定向来了解哪些行来自 流:2>&1
),而不是字符串,所以 _0791061 字符串可能包含一个字符串,每行 _079106 记录每行 stdout204 条 stdout204 记录,每行都包含一个 stdout25 字符串,代表 stdout204 行.请注意,根据 [System.Management.Automation.ErrorRecord]
的要求,字符串和错误记录都是通过 PowerShell 的成功输出流接收的)。2>&1
时相同的输出顺序;换句话说: 输出到控制台时,捕获的输出不反射(reflect)外部命令生成 stdout 和 stderr 行的顺序。 $_ -is [System.Management.Automation.ErrorRecord]
2>&1
错误代码类别(代码)和错误代码表示奇怪的是,这仅适用于第一个错误记录。Out-String
方法应用于每个输出对象,而不是通过管道传输到 At line:...
:+ CategoryInfo ...
;在 PS v3+ 中,您可以简化为:
.ToString()
(作为奖励,如果未捕获输出,即使在打印到控制台时也会产生正确的交错输出。)Out-String
将它们发送到 PowerShell 的错误流(作为奖励,如果没有捕获输出,即使打印到控制台也会产生正确的交错输出):$cmdOutput = <command> 2>&1 | ForEach-Object {
if ($_ -is [System.Management.Automation.ErrorRecord]) {
Write-Error $_
} else {
$_
}
}
从 PowerShell 7.1 开始,旁白重新传递参数 :
$cmdOutput = <command> 2>&1 | % { $_.ToString() }
字符的参数而言,将参数传递给外部程序被破坏。$cmdOutput = <command> 2>&1 | % ToString
和批处理文件等可执行文件的(非标准)引用需求。仅针对前一个问题,可能会进行修复(尽管修复将在类 Unix 平台上完成),如 this answer 中所述,其中还详细介绍了所有当前问题和解决方法。
如果可以选择安装第三方模块,则 this answer (
Write-Error
) 中的 "
函数提供了一个全面的解决方案。[1] 从 PowerShell 7.1 开始,PowerShell 在与外部程序 通信时只知道字符串。 PowerShell 管道中通常没有原始字节数据的概念。如果您想要从外部程序返回原始字节数据,您必须转至
msiexec.exe
(Windows) 或 ie
(Unix),保存到那里的文件,然后在 PowerShell 中读取该文件。有关更多信息,请参阅 Native
module。
关于powershell - 如何将输出从 PowerShell 中的外部进程捕获到变量中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8097354/