我想运行一个外部进程,并将它的命令输出捕获到PowerShell中的一个变量。我目前正在使用这个:
$params = "/verify $pc /domain:hosp.uhhg.org" start-process "netdom.exe" $params -WindowStyle Hidden -Wait
我已经确认该命令正在执行,但我需要将输出捕获到一个变量中。这意味着我不能使用-RedirectOutput,因为它只重定向到一个文件。
你有没有试过:
$OutputVariable = (Shell command) | Out-String
如果你想重定向错误输出,你必须做:
$cmdOutput = command 2>&1
或者,如果程序名中有空格:
$cmdOutput = & "command with spaces" 2>&1
或者试试这个。它将输出捕获到变量$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
注意,这个示例包括一个路径(以一个环境变量开始)。注意,引号必须包含路径和EXE文件,而不是参数!
不要忘记命令前面,但在引号之外的&字符。
&
错误输出也会被收集。
我花了一段时间来让这个组合工作,所以我想我会分享它。
我尝试了答案,但在我的情况下,我没有得到原始输出。相反,它被转换为一个PowerShell异常。
我得到的原始结果是:
$rawOutput = (cmd /c <command> 2`>`&1)
注意:问题中的命令使用Start-Process,这阻止了直接捕获目标程序的输出。一般来说,不使用Start-Process来同步执行控制台应用程序-只是调用它们直接,就像在任何shell中一样。这样做可以使应用程序的输出流连接到PowerShell的流,允许通过简单的赋值$output = netdom ...捕获它们的输出(并对stderr输出使用2>),如下所述。
Start-Process
$output = netdom ...
2>
基本上,从外部程序捕获输出,其工作原理与powershell本机命令相同(你可能需要复习一下如何执行外部程序;<command>是下面任何有效命令的占位符):
<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 如果<command>产生多个输出对象,则接收对象的数组,在外部程序表示字符串[1]数组,包含程序的输出行. c的情况下。
$cmdOutput
如果你想确保结果是always一个数组——即使只有一个对象输出,type-constrain变量作为数组([object[]]),或附上@(...)中的命令,array-subexpression运营商: [2]
[object[]]
@(...)
[array] $cmdOutput = <command> $cmdOutput = @(<command>) # alternative
相反,如果你想要$cmdOutput总是接收single -潜在多行- string,使用Out-String,尽管注意后面的换行符 必加 (GitHub issue #14444讨论了这个有问题的行为):
Out-String
# Note: Adds a trailing newline. $cmdOutput = <command> | Out-String
对于外部程序的调用——根据定义,它在PowerShell[1]中只返回字符串——你可以通过使用-join运营商来避免这种情况:
-join
# NO trailing newline. $cmdOutput = (<command>) -join "`n"
注意:为了简单起见,上面使用"`n"来创建unix风格的只使用lf的换行符,PowerShell在所有平台上都乐意接受这种换行符;如果需要适合于平台的换行符(Windows上是CRLF, Unix上是LF),请使用[Environment]::NewLine。
"`n"
[Environment]::NewLine
捕获在变量和打印到屏幕上中的输出:
<command> | Tee-Object -Variable cmdOutput # Note how the var name is NOT $-prefixed
-OutVariable
-ov
<command> -OutVariable cmdOutput # cmdlets and advanced functions only
[System.Collections.ArrayList]
要获取< em >多个< / em >命令的输出,可以使用子表达式($(...))或使用&或.调用脚本块({ ... }):
$(...)
.
{ ... }
$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 = & 'netdom.exe' ...
$(...)和& { ... } / . { ... }之间的关键区别是前者在返回之前将其作为一个整体返回,而后者将输出流,适合逐个管道处理。
& { ... }
. { ... }
重定向从根本上也是一样的(但请参阅下面的警告):
$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的类型系统之外操作,只返回strings通过它们的成功流(stdout);类似地,PowerShell只通过pipeline.[1]将发送字符串传递给外部程序
$OutVariable
[Console]::OutputEncoding
参见这个答案获取更多信息;这个答案讨论了Windows 10特性,该特性允许您在系统范围内将UTF-8设置为ANSI和OEM代码页。
[System.Object[]]
[System.String]
$cmdOutput = (<command>) -join [Environment]::NewLine
用2>&1将stderr合并为stdout,以便也捕获它作为成功流的一部分,附带警告:
2>&1
要做到这一点在源, 让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 < / p >
cmd.exe
sh
$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>结束后退出。
cmd /c
--%
%PATH%
Stderr输出被捕获为错误的记录 ([System.Management.Automation.ErrorRecord]),而不是字符串,因此输出数组可以包含mix 字符串(每个字符串表示一个标准输出行)和错误记录(每个记录表示一个标准输出行)。注意,根据2>&1的请求,字符串和错误记录都是通过PowerShell的成功输出流接收的)。
[System.Management.Automation.ErrorRecord]
$_ -is [System.Management.Automation.ErrorRecord]
如果你用Out-String捕获单个字符串中的整个输出, PowerShell将添加额外的行,因为错误记录的字符串表示包含额外的信息,如位置(At line:...)和类别(+ CategoryInfo ...);奇怪的是,这只适用于第一个错误记录。
At line:...
+ CategoryInfo ...
.ToString()
$cmdOutput = <command> 2>&1 | % { $_.ToString() }
$cmdOutput = <command> 2>&1 | % ToString
Write-Error
$cmdOutput = <command> 2>&1 | ForEach-Object { if ($_ -is [System.Management.Automation.ErrorRecord]) { Write-Error $_ } else { $_ } }
一个附加的re 参数-传递,从PowerShell 7.2.x开始:
将参数传递给外部程序是关于空字符串参数和包含嵌入式 "字符的参数。
"
msiexec.exe
仅针对前一个问题,可能会有修复(尽管修复将在__abc0类平台上完成),如这个答案中所讨论的,其中还详细介绍了所有当前问题和解决方法。
如果安装第三方模块是一个选项,Native模块 (Install-Module Native)中的ie函数提供了一个全面的解决方案。
Native
Install-Module Native
ie
从PowerShell 7.1开始,当与外部程序通信时,PowerShell只知道的字符串 。在PowerShell管道中通常没有原始字节数据的概念。如果你想从外部程序返回原始字节数据,你必须shell到cmd.exe /c (Windows)或sh -c (Unix),保存到文件there,然后在PowerShell中读取该文件。参见this answer了解更多信息。
cmd.exe /c
sh -c
这两种方法之间有微妙的区别(你可以结合使用),尽管它们通常并不重要:如果命令有no output, [array]类型约束方法会导致$null被存储在目标变量中,而在@(...)的情况下,它是一个空([object[])数组。此外,[array]类型约束意味着对同一变量的未来(非空)赋值也被强制转换为数组。
[array]
$null
[object[]
如果您所要做的只是捕获命令的输出,那么这将工作得很好。
我用它来改变系统时间,因为[timezoneinfo]::local总是产生相同的信息,即使后你已经对系统做了更改。这是我可以验证和记录时区更改的唯一方法:
[timezoneinfo]::local
$NewTime = (powershell.exe -command [timezoneinfo]::local) $NewTime | Tee-Object -FilePath $strLFpath\$strLFName -Append
这意味着我必须打开一个新的PowerShell会话来重新加载系统变量。
这招对我很管用:
$scriptOutput = (cmd /s /c $FilePath $ArgumentList)
我得到了以下工作:
$Command1="C:\\ProgramData\Amazon\Tools\ebsnvme-id.exe" $result = & invoke-Expression $Command1 | Out-String
美元的结果给你必要的
我使用以下方法:
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)