注意:问题中的命令使用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
请注意,如果<command> 产生超过 1 个输出对象,$cmdOutput 会收到一个对象的数组,在这种情况下外部程序表示一个字符串[1]数组,其中包含程序的输出行 .
如果您想确保结果是总是一个数组 - 即使只输出 一个 对象,输入-将变量约束为数组,或者将命令包装在@(),array-subexpression operator):
[array] $cmdOutput = <command> # or: $cmdOutput = @(<command>)
相比之下,如果您希望 $cmdOutput 始终收到 单行 - 可能是多行 - 字符串,请使用 Out-String ,但请注意总是会添加尾随换行符(GitHub issue #14444 讨论了这种有问题的行为):
# Note: Adds a trailing newline.
$cmdOutput = <command> | Out-String
通过调用外部程序——根据定义,它只会在PowerShell[1]中返回字符串——你可以通过使用-join operator 改为:
# NO trailing newline.
$cmdOutput = (<command>) -join "`n"
注意:为简单起见,上面使用 "`n" 创建 Unix 样式的 LF-only 换行符,PowerShell 很乐意在所有平台上接受它;如果您需要适合平台的换行符(Windows 上的 CRLF,Unix 上的 LF),请改用 [Environment]::NewLine。
捕获变量中的输出并打印到屏幕:
<command> | Tee-Object -Variable cmdOutput # Note how the var name is NOT $-prefixed
或者,如果<command>是一个cmdlet或高级函数,你可以使用common parameter
-OutVariable / -ov:
<command> -OutVariable cmdOutput # cmdlets and advanced functions only
请注意,对于-OutVariable,与其他场景不同,$cmdOutput始终是一个集合,即使只有一个对象是输出。具体来说,返回一个类似数组的 [System.Collections.ArrayList] 类型的实例。
有关此差异的讨论,请参阅 this GitHub issue。
要捕获多个命令的输出,请使用子表达式 ($(...)) 或使用 & 或 @ 调用脚本块 ({ ... }) 987654362@:
$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、外部程序、函数、别名)需要参数模式,这是&确保的。
$(...) 和& { ... } / . { ... } 之间的主要区别在于前者将所有输入收集到内存中然后将其作为一个整体返回,而后者流 em> 输出,适合一对一的流水线处理。
重定向从根本上也同样有效(但请参阅下面的注意事项):
$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 的类型系统之外运行,只会通过其成功流 (stdout) 返回 字符串;同样,PowerShell 仅通过管道发送字符串到外部程序。[1]
-
字符编码问题因此会发挥作用:
-
在通过管道向外部程序发送数据时,PowerShell 使用存储在$OutVariable 首选项变量中的编码;在 Windows PowerShell 中默认为 ASCII(!) 而在 PowerShell [Core] 中默认为 UTF-8。
-
在接收来自外部程序的数据时,PowerShell 使用存储在 [Console]::OutputEncoding 中的编码来解码数据,这在两个 PowerShell 版本中都默认为系统的活动 OEM代码页。
-
更多信息请见this answer; this answer 讨论了仍处于测试阶段(撰写本文时)的 Windows 10 功能,该功能允许您将 UTF-8 设置为系统范围内的 ANSI 和 OEM 代码页。
-
如果输出包含多于 1 行,PowerShell 默认将其拆分为 数组 字符串。更准确地说,输出行存储在 [System.Object[]] 类型的数组中,其元素是字符串 ([System.String])。
-
如果您希望输出是单行,可能是多行字符串,请使用-join 运算符(您也可以管道到Out-String,但总是添加一个尾随换行符):
$cmdOutput = (<command>) -join [Environment]::NewLine
-
将 stderr 与2>&1 合并到标准输出中,以便将其捕获为成功流的一部分,附带警告:
-
要做到这一点在源,让cmd.exe 处理重定向,使用以下成语(与sh 类似在类 Unix 平台上):
$cmdOutput = cmd /c <command> '2>&1' # *array* of strings (typically)
$cmdOutput = (cmd /c <command> '2>&1') -join "`r`n" # single string
-
cmd /c 使用命令<command> 调用cmd.exe 并在<command> 完成后退出。
-
注意2>&1 周围的单引号,它确保重定向被传递到cmd.exe 而不是被PowerShell 解释。
-
注意,涉及cmd.exe 意味着 its 用于转义字符和扩展环境变量的规则在默认情况下发挥作用,除了 PowerShell 自身的要求;在 PS v3+ 中,您可以使用特殊参数 --%(所谓的停止解析符号)关闭 PowerShell 对其余参数的解释,cmd.exe 样式的环境变量引用除外比如%PATH%。
-
请注意,由于您使用这种方法在源合并 stdout 和 stderr,您将无法区分源自 stdout 和源自 stderr 的行 在 PowerShell 中;如果您确实需要这种区别,请使用 PowerShell 自己的 2>&1 重定向 - 见下文。
-
使用 PowerShell 的 2>&1 重定向来了解哪些行来自哪个流:
-
Stderr 输出被捕获为 错误记录 ([System.Management.Automation.ErrorRecord]),而不是字符串,因此 输出数组可能包含 mix 字符串(每个字符串代表一个标准输出行)和错误记录(每个记录代表一个标准错误行)。请注意,根据2>&1 的要求,字符串和错误记录都是通过 PowerShell 的 success 输出流接收的)。
-
注意:以下仅适用于 Windows PowerShell - 这些问题已在 PowerShell [Core] v6+中得到纠正strong>,尽管下面显示的按对象类型过滤技术 ($_ -is [System.Management.Automation.ErrorRecord]) 在那里也很有用。
-
在控制台中,错误记录以红色打印,而第一个默认产生多行显示,同理cmdlet 的非终止错误将显示的格式; 后续错误记录也以红色打印,但仅在单行上打印其错误消息。
-
当输出到控制台时,字符串通常在输出数组中排在第一个,然后是错误记录(至少在一个一批 stdout/stderr 行“同时”输出),但幸运的是,当您捕获输出时,它会正确交错,使用与您相同的输出顺序没有2>&1就会得到;换句话说:输出到控制台时,捕获的输出不反映外部命令生成stdout和stderr行的顺序。
-
如果您使用Out-String在单个字符串中捕获整个输出,PowerShell 将添加额外的行,因为错误记录的字符串表示包含额外的信息,例如位置(At line:...)和类别(+ CategoryInfo ...);奇怪的是,这只适用于 first 错误记录。
-
要解决此问题,请将.ToString() 方法应用于每个输出对象,而不是通过管道连接到Out-String:
$cmdOutput = <command> 2>&1 | % { $_.ToString() };
在 PS v3+ 中,您可以简化为:
$cmdOutput = <command> 2>&1 | % ToString
(作为奖励,如果没有捕获输出,即使在打印到控制台时也会产生正确的交错输出。)
-
或者,过滤错误记录 out 并使用 Write-Error 将它们发送到 PowerShell 的错误流(作为奖励,如果未捕获输出,这会产生即使在打印到控制台时也能正确交错输出):
$cmdOutput = <command> 2>&1 | ForEach-Object {
if ($_ -is [System.Management.Automation.ErrorRecord]) {
Write-Error $_
} else {
$_
}
}
关于 参数传递,从 PowerShell 7.1 开始:
如this answer 中所述,可能即将修复前一个问题(尽管该修复将在类似 Unix 的平台上完成),其中还详细说明了所有当前问题和解决方法。
如果安装第三方模块是一个选项,Native module (Install-Module Native) 中的ie 函数提供了一个全面 解决方案。 p>
[1] 从 PowerShell 7.1 开始,PowerShell 在与外部程序通信时只知道字符串。 PowerShell 管道中通常没有 原始字节数据 的概念。如果您想从外部程序返回原始字节数据,您必须转至 cmd.exe /c (Windows) 或 sh -c (Unix),保存到文件那里,然后在 PowerShell 中读取该文件.更多信息请参见this answer。