注意:问题中的命令使用Start-Process
,这会阻止直接捕获目标程序的输出。通常, 不要用于Start-Process
同步执行控制台应用程序-就像在任何shell中一样,直接调用它们即可。这样可以使应用程序保持与调用控制台的标准流连接,从而允许通过简单的分配捕获其输出$output = netdom ...
,如下所述。
从根本上讲,从外部实用程序捕获输出与使用PowerShell本地命令的工作原理相同(您可能需要重新了解如何执行外部工具):
$cmdOutput = <command> # captures the command's success stream / stdout
请注意,如果产生一个以上的输出对象,则$cmdOutput
接收一个对象数组,这对于外部程序而言是指包含程序输出行的字符串数组。
如果您希望始终收到一个单行(可能是多行)字符串,请使用<command>
$cmdOutput
$cmdOutput = <command> | Out-String
要捕获变量中的输出并打印到屏幕:
<command> | Tee-Object -Variable cmdOutput # Note how the var name is NOT $-prefixed
或者,如果<command>
是cmdlet或高级功能,则可以使用公共参数
-OutVariable
/-ov
:
<command> -OutVariable cmdOutput # cmdlets and advanced functions only
请注意,-OutVariable
不像在其他情况下, $cmdOutput
是总是一个集合,即使只有一个对象被输出。具体来说,将[System.Collections.ArrayList]
返回类似数组类型的实例。
有关此差异的讨论,请参见此GitHub问题。
为了捕获从输出多个命令,既可以使用子表达式($(...)
)或调用脚本块({ ... }
)与&
或.
:
$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.
外部程序的注意事项:
外部程序,因为它们在PowerShell的类型系统之外运行,因此只能通过其成功流(stdout)返回字符串。
如果输出包含多于1行,PowerShell默认将其拆分为字符串数组。更准确地说,输出行存储在类型为数组的类型中,[System.Object[]]
其元素为字符串([System.String]
)。
如果您希望输出是单个(可能是多行)字符串,请通过以下管道传递给Out-String
:
$cmdOutput = <command> | Out-String
使用重定向stderr到stdout2>&1
,以便也将其捕获为成功流的一部分,附带警告:
为了使2>&1
合并输出和错误的来源,让cmd.exe
处理重定向,使用下列成语:
$cmdOutput = cmd /c <command> '2>&1' # *array* of strings (typically)
$cmdOutput = cmd /c <command> '2>&1' | Out-String # single string
cmd /c
cmd.exe
使用command 调用<command>
并在<command>
完成后退出。
- 注意周围的单引号
2>&1
,以确保将重定向传递给cmd.exe
PowerShell而不是由PowerShell解释。
请注意,默认情况下,除PowerShell自身的要求外,包含cmd.exe
是指其转义字符和扩展环境变量的规则起作用;在PS v3 +中,您可以使用特殊参数--%
(所谓的停止解析符号)来关闭PowerShell其余参数的解释,但cmd.exe
样式风格的环境变量引用除外%PATH%
。
请注意,由于您是使用这种方法在源头合并stdout和stderr 的,因此您将无法在PowerShell中区分stdout起源行和stderr起源行。如果确实需要这种区别,请使用PowerShell自己的2>&1
重定向-参见下文。
使用PowerShell的 2>&1
重定向可以知道哪些行来自哪个流:
stderr的输出被捕获作为错误记录([System.Management.Automation.ErrorRecord]
),而不是字符串,所以输出阵列可以包含混合的串并(表示标准输出线中的每个字符串)错误记录(代表stderr的行中的每个记录)。请注意,按照的要求2>&1
,字符串和错误记录都是通过PowerShell的成功输出流接收的。
在控制台中,错误记录以红色打印,默认情况下,第一个记录将以多行显示的形式显示cmdlet的非终止错误;后续错误记录也以红色打印,但仅将错误消息打印在一行上。
当输出到控制台时,字符串通常来第一输出数组中,随后由错误记录(在一个批次的stdout / stderr的线输出中至少“同时”),但是,幸运的是,当你捕获输出,使用与您不用的输出顺序相同的输出顺序进行适当的交错2>&1
; 换句话说:当输出到控制台时,捕获的输出不反映外部命令生成stdout和stderr行的顺序。
如果使用捕获单个字符串中的全部输出Out-String
,PowerShell将添加额外的行,因为错误记录的字符串表示形式包含额外的信息,例如位置(At line:...
)和类别(+ CategoryInfo ...
);奇怪的是,这仅适用于第一个错误记录。
- 要变通解决此问题,请将该
.ToString()
方法应用于每个输出对象,而不是对Out-String
:进行管道传递
$cmdOutput = <command> 2>&1 | % { $_.ToString() }
;
在PS v3 +中,您可以简化为:(
$cmdOutput = <command> 2>&1 | % ToString
此外,如果未捕获输出,即使在打印到控制台时,也会产生正确的交错输出。)
或者,过滤掉错误记录并将其发送到PowerShell的错误流中Write-Error
(此外,如果未捕获输出,即使在打印到控制台时,也会产生正确的交错输出):
$cmdOutput = <command> 2>&1 | ForEach-Object {
if ($_ -is [System.Management.Automation.ErrorRecord]) {
Write-Error $_
} else {
$_
}
}
Start-Process
来同步执行(定义上是外部的)控制台应用程序-就像在任何shell中一样,直接调用它们即可;即:netdom /verify $pc /domain:hosp.uhhg.org
。这样做可以使应用程序保持与调用控制台的标准流连接,从而允许通过简单的分配捕获其输出$output = netdom ...
。下面给出的大多数答案都隐含地放弃Start-Process
了直接执行。