Powershell:将外部命令输出通过管道传输到另一个外部命令

Posted

技术标签:

【中文标题】Powershell:将外部命令输出通过管道传输到另一个外部命令【英文标题】:Powershell: Pipe external command output to another external command 【发布时间】:2013-10-13 19:06:06 【问题描述】:

我怎样才能让 PowerShell 理解这种类型的东西:

Robocopy.exe | Find.exe "Started"

旧的命令处理器给出了结果,但我对如何在 PowerShell 中执行此操作感到困惑:

&robocopy | find.exe "Started"                #error
&robocopy | find.exe @("Started")             #error
&robocopy @("|", "find.exe","Started")        #error
&robocopy | &find @("Started")                #error
&(robocopy | find "Started")                  #error

本质上,我想将一个外部命令的输出通过管道传输到另一个外部命令。实际上,我将调用 flac.exe 并将其导入 lame.exe 以将 FLAC 转换为 MP3。

干杯

【问题讨论】:

如果 .exe 不是为接受管道而设计的,你就不能.. Find.exe 被设计为接受管道,就像 lame.exe 一样。我得到的任何错误都是来自 Powershell 不理解我的意思。您可以尝试在 cmd.exe 中查找示例 【参考方案1】:

tl;dr

# Note the nested quoting. CAVEAT: May break in the future.
robocopy.exe | find.exe '"Started"'    

# Alternative. CAVEAT: doesn't support *variable references* after --%
robocopy.exe | find.exe --% "Started"

# *If available*, use PowerShell's equivalent of an external program.
# In lieu of `findstr.exe`, you can use Select-String (whose built-in alias is scs):
# Note: Outputs are *objects* describing the matching lines.
#       To get just the lines, pipe to | % ToString 
#       or - in PowerShell 7+ _ use -Raw
robocopy.exe | sls Started

有关解释,请继续阅读。


PowerShell 确实支持与外部程序之间的管道。

这里的问题是参数解析和传递之一:find.exe 有一个奇怪的要求,即它的 搜索词必须用 literal 双引号括起来。

cmd.exe 中,简单的双引号就足够了:find.exe "Started"

相比之下,默认情况下,PowerShell 在传递参数之前预先解析参数,如果逐字参数值不包含空格,则 去除 括起来的引号 em>,这样find.exe 只能看到Started没有双引号,导致错误。

有三种方法可以解决这个问题:

PS v3+(仅当您的参数只有文字和/或环境变量时才可以选择):--%,@987654321 @,告诉 PowerShell 将命令行的 rest按原样传递给目标程序(引用环境变量,如果有的话,cmd-风格 (%<var>%)):robocopy.exe | find.exe --% "Started"

This answer 详细说明了 --% 的限制。

PS v2 也可以,或者如果您需要在参数中使用 PowerShell 变量:应用 PowerShell 引用的外层(PowerShell 将去掉单引号并将字符串的内容按原样传递给find.exe,并保持双引号不变):robocopy.exe | find.exe '"Started"'

警告:只有破坏行为,这种技术才有效。如果此行为得到修复(修复可能需要选择加入),上述操作将不再有效,因为 PowerShell 将在幕后传递 ""Started"",这会中断调用 - 请参阅 this answer 了解更多信息。李>

如果有类似的 PowerShell 命令可用,请使用它,这样可以避免所有引用问题。在这种情况下,Select-String cmdlet ,可以使用PowerShell对findstr.exe的更多powershell模拟,如上图。

【讨论】:

【参考方案2】:

@Jobbo:​​cmd 和 PowerShell 是两个不同的 shell。有时可以混合它们,但正如您从 Shay 的回答中意识到的那样,它不会让您走得太远。但是,您可能在这里问错了问题。

大多数时候,您尝试解决的问题(例如通过管道连接到 find.exe)甚至都不是必需的。 你在 Powershell 中确实有 find.exe 的等价物,实际上是更强大的版本:select-string

您始终可以运行命令并将结果分配给变量。

$results = Robocopy c:\temp\a1 c:\temp\a2 /MIR

结果将是 STRING 类型,您可以使用许多工具对其进行切片和切块。

PS > $results |select-string "started"

  Started : Monday, October 07, 2013 8:15:50 PM

【讨论】:

【参考方案3】:

通过 cmd 调用它:

PS> cmd /c 'Robocopy.exe | Find.exe "Started"'

【讨论】:

这行得通,但是如果我的命令有很多参数传递给它们(它们确实如此),那么我会失去在数组中传递参数的好处,比如@(,,,)?有什么方法可以在不涉及 cmd.exe 的情况下做到这一点? 这行得通,但读起来却是一场噩梦:&cmd @("/c",@('robocopy','|','find "Started"'))在将其全部构建为一个字符串和嵌套数组之间? 我知道这种感觉...我会在 cmd 文件中编写超过一行的命令并执行它。 哇,这真是衣衫褴褛。我想知道我是否可以使用管道连接到 Out-Host 或其他东西?我发现我可以使用 Tee-Object 将 Robocopy 的输出转换为变量,但随后只需将变量通过管道传输到外部命令中,例如:$out | &find.exe 这似乎是不可能的

以上是关于Powershell:将外部命令输出通过管道传输到另一个外部命令的主要内容,如果未能解决你的问题,请参考以下文章

将 PowerShell 的默认输出编码更改为 UTF-8

将 ffmpeg 输出通过管道传输到命名管道

在 Vim 中将缓冲区管道传输到外部命令

Windows CLI:将列表通过管道传输到 awk 并用外部文件中的文本替换文本并写入 output.txt

在 CMD 和 PowerShell 中管道时的不同行为和输出

在 CMD 和 PowerShell 中管道时的不同行为和输出