[英]How do I capture the output into a variable from an external process in PowerShell?
I'd like to run an external process and capture it's command output to a variable in PowerShell.我想运行一个外部进程并将它的命令输出捕获到 PowerShell 中的变量。 I'm currently using this:
我目前正在使用这个:
$params = "/verify $pc /domain:hosp.uhhg.org"
start-process "netdom.exe" $params -WindowStyle Hidden -Wait
I've confirmed the command is executing but I need to capture the output into a variable.我已经确认命令正在执行,但我需要将输出捕获到一个变量中。 This means I can't use the -RedirectOutput because this only redirects to a file.
这意味着我不能使用 -RedirectOutput 因为这只会重定向到一个文件。
Note: The command in the question uses Start-Process
, which prevents direct capturing of the target program's output.注意:问题中的命令使用
Start-Process
,这会阻止直接捕获目标程序的输出。 Generally, do not use Start-Process
to execute console applications synchronously - just invoke them directly , as in any shell.通常,不要使用
Start-Process
同步执行控制台应用程序 - 只需直接调用它们,就像在任何 shell 中一样。 Doing so keeps the application connected to the calling console's standard streams, allowing its output to be captured by simple assignment $output = netdom ...
, as detailed below.这样做使应用程序连接到调用控制台的标准流,允许通过简单的赋值
$output = netdom ...
捕获其输出,如下详述。
Fundamentally , capturing output from external programs works the same as with PowerShell-native commands (you may want a refresher on how to execute external programs ; <command>
is a placeholder for any valid command below):从根本上说,从外部程序捕获输出的工作方式与 PowerShell 本地命令相同(您可能需要复习一下如何执行外部程序;
<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
Note that $cmdOutput
receives an array of objects if <command>
produces more than 1 output object , which in the case of an external program means a string [1] array containing the program's output lines .请注意,如果
<command>
产生1 个以上的输出对象,则$cmdOutput
接收一个对象数组,在外部程序的情况下,这意味着包含程序输出行的字符串[1]数组。
If you want to make sure that the result is always an array - even if only one object is output, type-constrain the variable as an array, or wrap the command in @()
, the array-subexpression operator ):如果您想确保结果始终是一个数组- 即使只输出一个对象,请将变量类型约束为数组,或将命令包装在
@()
,即数组子表达式运算符):
[array] $cmdOutput = <command> # or: $cmdOutput = @(<command>)
By contrast, if you want $cmdOutput
to always receive a single - potentially multi-line - string , use Out-String
, though note that a trailing newline is invariably added ( GitHub issue #14444 discusses this problematic behavior):相比之下,如果您希望
$cmdOutput
始终接收单行(可能是多行) string ,请使用Out-String
,但请注意,总是添加尾随换行符( GitHub 问题 #14444讨论了这种有问题的行为):
# Note: Adds a trailing newline.
$cmdOutput = <command> | Out-String
With calls to external programs - which by definition only ever return strings in PowerShell [1] - you can avoid that by using the -join
operator instead:通过调用外部程序- 根据定义,它只在 PowerShell [1] 中返回字符串- 您可以通过使用
-join
运算符来避免这种情况:
# NO trailing newline.
$cmdOutput = (<command>) -join "`n"
Note: For simplicity, the above uses "`n"
to create Unix-style LF-only newlines, which PowerShell happily accepts on all platforms;注意:为简单起见,上面使用
"`n"
来创建 Unix 风格的 LF-only 换行符,PowerShell 很乐意在所有平台上接受; if you need platform-appropriate newlines (CRLF on Windows, LF on Unix), use [Environment]::NewLine
instead.如果您需要适合平台的换行符(Windows 上为 CRLF,Unix 上为 LF),请改用
[Environment]::NewLine
。
To capture output in a variable and print to the screen :要捕获变量中的输出并打印到屏幕:
<command> | Tee-Object -Variable cmdOutput # Note how the var name is NOT $-prefixed
Or, if <command>
is a cmdlet or advanced function, you can use common parameter或者,如果
<command>
是cmdlet或高级函数,则可以使用通用参数
-OutVariable
/ -ov
: -OutVariable
/ -ov
:
<command> -OutVariable cmdOutput # cmdlets and advanced functions only
Note that with -OutVariable
, unlike in the other scenarios, $cmdOutput
is always a collection , even if only one object is output.请注意,使用
-OutVariable
,与其他情况不同, $cmdOutput
始终是一个集合,即使只输出一个对象。 Specifically, an instance of the array-like [System.Collections.ArrayList]
type is returned.具体来说,返回一个类似数组的
[System.Collections.ArrayList]
类型的实例。
See this GitHub issue for a discussion of this discrepancy.有关此差异的讨论,请参阅此 GitHub 问题。
To capture the output from multiple commands , use either a subexpression ( $(...)
) or call a script block ( { ... }
) with &
or .
要捕获多个命令的输出,请使用子表达式 (
$(...)
) 或使用&
或 调用脚本块 ( { ... }
) .
: :
$cmdOutput = $(<command>; ...) # subexpression
$cmdOutput = & {<command>; ...} # script block with & - creates child scope for vars.
$cmdOutput = . {<command>; ...} # script block with . - no child scope
Note that the general need to prefix with &
(the call operator) an individual command whose name/path is quoted - eg, $cmdOutput = & 'netdom.exe' ...
- is not related to external programs per se (it equally applies to PowerShell scripts), but is a syntax requirement : PowerShell parses a statement that starts with a quoted string in expression mode by default, whereas argument mode is needed to invoke commands (cmdlets, external programs, functions, aliases), which is what &
ensures.请注意,一般需要以
&
(调用运算符)作为前缀的单个命令的名称/路径被引用- 例如, $cmdOutput = & 'netdom.exe' ...
- 与外部程序本身无关(它同样适用于 PowerShell 脚本),但有一个语法要求:PowerShell 默认在表达式模式下解析以带引号的字符串开头的语句,而调用命令(cmdlet、外部程序、函数、别名)需要参数模式,这就是&
确保。
The key difference between $(...)
and & { ... }
/ . { ... }
$(...)
和& { ... }
/ 之间的主要区别. { ... }
. { ... }
is that the former collects all input in memory before returning it as a whole, whereas the latter stream the output, suitable for one-by-one pipeline processing. . { ... }
是前者将所有输入收集在内存中,然后作为一个整体返回,而后者将输出流式处理,适用于一对一的流水线处理。
Redirections also work the same, fundamentally (but see caveats below):重定向从根本上也是一样的(但请参阅下面的警告):
$cmdOutput = <command> 2>&1 # redirect error stream (2) to success stream (1)
However, for external commands the following is more likely to work as expected:但是,对于外部命令,以下更有可能按预期工作:
$cmdOutput = cmd /c <command> '2>&1' # Let cmd.exe handle redirection - see below.
Considerations specific to external programs:特定于外部程序的注意事项:
External programs , because they operate outside PowerShell's type system, only ever return strings via their success stream (stdout);外部程序,因为它们在 PowerShell 的类型系统之外运行,所以只通过它们的成功流 (stdout)返回字符串; similarly, PowerShell only ever sends strings to external programs via the pipeline.
同样,PowerShell 只通过管道向外部程序发送字符串。 [1]
[1]
On sending data via the pipeline to external programs, PowerShell uses the encoding stored in the $OutVariable
preference variable;在通过管道向外部程序发送数据时,PowerShell 使用存储在
$OutVariable
首选项变量中的编码; which in Windows PowerShell defaults to ASCII(!) and in PowerShell [Core] to UTF-8.在 Windows PowerShell 中默认为 ASCII(!),在 PowerShell [Core] 中默认为 UTF-8。
On receiving data from an external program, PowerShell uses the encoding stored in [Console]::OutputEncoding
to decode the data, which in both PowerShell editions defaults to the system's active OEM code page.从外部程序接收数据时,PowerShell 使用存储在
[Console]::OutputEncoding
的编码对数据进行解码,这在两个 PowerShell 版本中都默认为系统的活动OEM代码页。
See this answer for more information;有关更多信息,请参阅此答案; this answer discusses the still-in-beta (as of this writing) Windows 10 feature that allows you to set UTF-8 as both the ANSI and the OEM code page system-wide.
此答案讨论了仍处于测试阶段(截至撰写本文时)的 Windows 10 功能,该功能允许您将 UTF-8 设置为系统范围内的 ANSI 和 OEM 代码页。
If the output contains more than 1 line , PowerShell by default splits it into an array of strings .如果输出包含多于 1 行,PowerShell 默认将其拆分为字符串数组。 More accurately, the output lines are stored in an array of type
[System.Object[]]
whose elements are strings ( [System.String]
).更准确地说,输出行存储在类型为
[System.Object[]]
的数组中,其元素是字符串 ( [System.String]
)。
If you want the output to be a single , potentially multi-line string , use the -join
operator (you can alternatively pipe to Out-String
, but that invariably adds a trailing newline):如果您希望输出是单个的,可能是多行的string ,请使用
-join
运算符(您也可以通过管道连接到Out-String
,但这总是会添加一个尾随换行符):
$cmdOutput = (<command>) -join [Environment]::NewLine
Merging stderr into stdout with 2>&1
, so as to also capture it as part of the success stream, comes with caveats :使用
2>&1
将stderr合并到 stdout 中,以便也将其捕获为成功流的一部分,但需要注意:
To do this at the source , let cmd.exe
handle the redirection , using the following idioms (works analogously with sh
on Unix-like platforms):要在源代码中执行此操作,请让
cmd.exe
使用以下习语处理重定向(在类 Unix 平台上与sh
类似):
$cmdOutput = cmd /c <command> '2>&1' # *array* of strings (typically)
$cmdOutput = (cmd /c <command> '2>&1') -join "`r`n" # single string
cmd /c
invokes cmd.exe
with command <command>
and exits after <command>
has finished. cmd /c
使用命令<command>
调用cmd.exe
并在<command>
完成后退出。
Note the single quotes around 2>&1
, which ensures that the redirection is passed to cmd.exe
rather than being interpreted by PowerShell.请注意
2>&1
周围的单引号,这可确保将重定向传递给cmd.exe
而不是由 PowerShell 解释。
Note that involving cmd.exe
means that its rules for escaping characters and expanding environment variables come into play, by default in addition to PowerShell's own requirements;注意,涉及
cmd.exe
意味着它的转义字符和扩展环境变量的规则发挥作用,默认情况下,除了 PowerShell 自己的要求; in PS v3+ you can use special parameter --%
(the so-called stop-parsing symbol ) to turn off interpretation of the remaining parameters by PowerShell, except for cmd.exe
-style environment-variable references such as %PATH%
.在 PS v3+ 中,您可以使用特殊参数
--%
(所谓的停止解析符号)来关闭 PowerShell 对其余参数的解释,除了cmd.exe
风格的环境变量引用,例如%PATH%
。
Note that since you're merging stdout and stderr at the source with this approach, you won't be able to distinguish between stdout-originated and stderr-originated lines in PowerShell;请注意,由于您使用这种方法在源代码处合并 stdout 和 stderr ,因此您将无法在 PowerShell 中区分源自 stdout 的行和源自 stderr 的行; if you do need this distinction, use PowerShell's own
2>&1
redirection - see below.如果您确实需要这种区别,请使用 PowerShell 自己的
2>&1
重定向 - 见下文。
Use PowerShell's 2>&1
redirection to know which lines came from what stream :使用PowerShell 的
2>&1
重定向来了解哪些行来自哪个流:
Stderr output is captured as error records ( [System.Management.Automation.ErrorRecord]
), not strings, so the output array may contain a mix of strings (each string representing a stdout line) and error records (each record representing a stderr line) . stderr输出被捕获作为错误记录(
[System.Management.Automation.ErrorRecord]
而不是字符串,所以输出阵列可以包含字符串的混合物(每个字符串代表一个标准输出线)和错误记录(代表stderr的线的每个记录) 。 Note that, as requested by 2>&1
, both the strings and the error records are received through PowerShell's success output stream).请注意,根据
2>&1
,字符串和错误记录都是通过 PowerShell 的成功输出流接收的)。
Note: The following only applies to Windows PowerShell - these problems have been corrected in PowerShell [Core] v6+ , though the filtering technique by object type shown below ( $_ -is [System.Management.Automation.ErrorRecord]
) can also be useful there.注意:以下仅适用于Windows PowerShell - 这些问题已在PowerShell [Core] v6+ 中得到纠正,尽管下面显示的按对象类型过滤技术 (
$_ -is [System.Management.Automation.ErrorRecord]
) 也很有用那里。
In the console, the error records print in red , and the 1st one by default produces multi-line display, in the same format that a cmdlet's non-terminating error would display;在控制台中,错误记录以red打印,第一个默认情况下生成多行显示,格式与 cmdlet 的非终止错误将显示的格式相同; subsequent error records print in red as well, but only print their error message , on a single line .
随后的错误记录也以红色打印,但仅在一行上打印其错误消息。
When outputting to the console , the strings typically come first in the output array, followed by the error records (at least among a batch of stdout/stderr lines output "at the same time"), but, fortunately, when you capture the output, it is properly interleaved , using the same output order you would get without 2>&1
;输出到控制台时,字符串通常首先出现在输出数组中,然后是错误记录(至少在“同时”输出的一批 stdout/stderr 行中),但幸运的是,当您捕获输出时,它是正确交错的,使用与没有
2>&1
相同的输出顺序; in other words: when outputting to the console , the captured output does NOT reflect the order in which stdout and stderr lines were generated by the external command.换句话说:当输出到控制台时,捕获的输出不反映外部命令生成 stdout 和 stderr 行的顺序。
If you capture the entire output in a single string with Out-String
, PowerShell will add extra lines , because the string representation of an error record contains extra information such as location ( At line:...
) and category ( + CategoryInfo ...
);如果使用
Out-String
在单个字符串中捕获整个输出, PowerShell 将添加额外的行,因为错误记录的字符串表示包含额外的信息,例如位置( At line:...
)和类别( + CategoryInfo ...
); curiously, this only applies to the first error record.奇怪的是,这仅适用于第一个错误记录。
To work around this problem, apply the .ToString()
method to each output object instead of piping to Out-String
:要解决此问题,请将
.ToString()
方法应用于每个输出对象,而不是管道到Out-String
:
$cmdOutput = <command> 2>&1 | % { $_.ToString() }
$cmdOutput = <command> 2>&1 | % { $_.ToString() }
; $cmdOutput = <command> 2>&1 | % { $_.ToString() }
;
in PS v3+ you can simplify to:在 PS v3+ 中,您可以简化为:
$cmdOutput = <command> 2>&1 | % ToString
(As a bonus, if the output isn't captured, this produces properly interleaved output even when printing to the console.) (作为奖励,如果未捕获输出,即使在打印到控制台时也会产生正确的交错输出。)
Alternatively, filter the error records out and send them to PowerShell's error stream with Write-Error
(as a bonus, if the output isn't captured, this produces properly interleaved output even when printing to the console):或者,过滤掉错误记录,并使用
Write-Error
将它们发送到 PowerShell 的错误流(作为奖励,如果未捕获输出,即使打印到控制台也会产生正确的交错输出):
$cmdOutput = <command> 2>&1 | ForEach-Object {
if ($_ -is [System.Management.Automation.ErrorRecord]) {
Write-Error $_
} else {
$_
}
}
An aside re argument-passing , as of PowerShell 7.1 :从 PowerShell 7.1 开始,重新传递参数:
Passing arguments to external programs is broken with respect to empty-string arguments and arguments that contain embedded "
characters.对于空字符串参数和包含嵌入的
"
字符的参数,将参数传递给外部程序会被破坏。
Additionally, the (nonstandard) quoting needs of executables such as msiexec.exe
and batch files aren't accommodated.此外,不满足诸如
msiexec.exe
和批处理文件之类的可执行文件的(非标准)引用需求。
For the former problem only, a fix may be coming (though the fix would be complete on Unix -like platforms), as discussed in this answer , which also details all the current problems and workarounds.仅针对前一个问题,可能会进行修复(尽管修复将在类 Unix平台上完成),如本答案中所述,其中还详细介绍了所有当前问题和解决方法。
If installing a third-party module is an option, the ie
function from the Native
module ( Install-Module Native
) offers a comprehensive solution.如果安装第三方模块是一个选项,来自
Native
模块( Install-Module Native
) 的ie
功能提供了一个全面的解决方案。
[1] As of PowerShell 7.1, PowerShell knows only strings when communicating with external programs . [1]从 PowerShell 7.1 开始,PowerShell 在与外部程序通信时只知道字符串。 There is generally no concept of raw byte data in a PowerShell pipeline.
PowerShell 管道中通常没有原始字节数据的概念。 If you want raw byte data returned from an external program, you must shell out to
cmd.exe /c
(Windows) or sh -c
(Unix), save to a file there , then read that file in PowerShell.如果您想要从外部程序返回原始字节数据,您必须将 shell 输出到
cmd.exe /c
(Windows) 或sh -c
(Unix),保存到那里的文件,然后在 PowerShell 中读取该文件。 See this answer for more information.有关更多信息,请参阅此答案。
你有没有尝试过:
$OutputVariable = (Shell command) | Out-String
If you want to redirect the error output as well, you have to do:如果您还想重定向错误输出,则必须执行以下操作:
$cmdOutput = command 2>&1
Or, if the program name has spaces in it:或者,如果程序名称中有空格:
$cmdOutput = & "command with spaces" 2>&1
Or try this.或者试试这个。 It will capture output into variable $scriptOutput:
它会将输出捕获到变量 $scriptOutput 中:
& "netdom.exe" $params | Tee-Object -Variable scriptOutput | Out-Null
$scriptOutput
Another real-life example:另一个现实生活中的例子:
$result = & "$env:cust_tls_store\Tools\WDK\x64\devcon.exe" enable $strHwid 2>&1 | Out-String
Notice that this example includes a path (which begins with an environment variable).请注意,此示例包含一个路径(以环境变量开头)。 Notice that the quotes must surround the path and the EXE file, but not the parameters!
请注意,引号必须将路径和 EXE 文件括起来,而不是参数!
Note: Don't forget the &
character in front of the command, but outside of the quotes.注意:不要忘记命令前面的
&
字符,但在引号之外。
The error output is also collected.错误输出也会被收集。
It took me a while to get this combination working, so I thought that I would share it.我花了一段时间才让这个组合起作用,所以我想我会分享它。
I tried the answers, but in my case I did not get the raw output.我尝试了答案,但就我而言,我没有得到原始输出。 Instead it was converted to a PowerShell exception.
相反,它被转换为 PowerShell 异常。
The raw result I got with:我得到的原始结果:
$rawOutput = (cmd /c <command> 2`>`&1)
这件事对我有用:
$scriptOutput = (cmd /s /c $FilePath $ArgumentList)
I got the following to work:我得到了以下工作:
$Command1="C:\\ProgramData\Amazon\Tools\ebsnvme-id.exe"
$result = & invoke-Expression $Command1 | Out-String
$result gives you the needful $result给你需要的
If all you are trying to do is capture the output from a command, then this will work well.如果您要做的只是捕获命令的输出,那么这将很有效。
I use it for changing system time, as [timezoneinfo]::local
always produces the same information, even after you have made changes to the system.我用它来改变系统时间,因为
[timezoneinfo]::local
总是产生相同的信息,即使在你对系统进行了更改之后也是如此。 This is the only way I can validate and log the change in time zone:这是我可以验证和记录时区更改的唯一方法:
$NewTime = (powershell.exe -command [timezoneinfo]::local)
$NewTime | Tee-Object -FilePath $strLFpath\$strLFName -Append
Meaning that I have to open a new PowerShell session to reload the system variables.这意味着我必须打开一个新的PowerShell会话来重新加载系统变量。
I use the following:我使用以下内容:
Function GetProgramOutput([string]$exe, [string]$arguments)
{
$process = New-Object -TypeName System.Diagnostics.Process
$process.StartInfo.FileName = $exe
$process.StartInfo.Arguments = $arguments
$process.StartInfo.UseShellExecute = $false
$process.StartInfo.RedirectStandardOutput = $true
$process.StartInfo.RedirectStandardError = $true
$process.Start()
$output = $process.StandardOutput.ReadToEnd()
$err = $process.StandardError.ReadToEnd()
$process.WaitForExit()
$output
$err
}
$exe = "C:\Program Files\7-Zip\7z.exe"
$arguments = "i"
$runResult = (GetProgramOutput $exe $arguments)
$stdout = $runResult[-2]
$stderr = $runResult[-1]
[System.Console]::WriteLine("Standard out: " + $stdout)
[System.Console]::WriteLine("Standard error: " + $stderr)
什么对我有用,并且在使用外部命令以及标准错误和标准输出流都可能是运行命令(或它们的组合)的结果时起作用,如下所示:
$output = (command 2>&1)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.