# WRONG
$command = "a.xml b.xml c.xml"; junit-merge $command
导致命令行junit-merge "a.xml b.xml c.xml"
[1],即它将字符串a.xml b.xml c.xml
作为单个参数传递给junit-merge
,这不是意图。
PowerShell在这方面的行为不像 POSIX-like shell :bash
在bash
中,变量的值$command
- 由于被引用未引用- 将受到分词(所谓的shell 扩展之一)的影响,并且确实会导致3 个不同的参数(尽管即使存在基于数组的调用也会更可取)。
PowerShell 不支持bash
-like shell 扩展[2];它具有不同的,通常更灵活的构造,例如下面讨论的喷溅技术。
相反,将您的参数定义为array 的单个元素,正如justnotme建议的那样:
# Define the *array* of *individual* arguments.
$command = "a.xml", "b.xml", "c.xml"
# Pass the array to junit-merge, which causes PowerShell
# to pass its elements as *individual arguments*; it is the equivalent of:
# junit-merge a.xml b.xml c.xml
junit-merge $command
这是一种称为splatting的 PowerShell 技术的应用程序,您可以在其中指定要通过变量传递给命令的参数:
要么(通常仅用于外部程序,如您的情况):
作为一个参数数组,作为位置参数单独传递,如上所示。
或者(更常见的是在调用PowerShell 命令时):
作为传递命名参数值的哈希表,您必须将变量引用中的符号替换为; 例如,在您的情况下;例如,以下等效于调用:$
@
@command
Get-ChildItem C:\ -Directory
$paramVals = @{ LiteralPath = 'C:\'; Directory = $true }; Get-ChildItem @paramVals
警告基于数组的喷溅:
由于此 GitHub 问题中详述的错误,PowerShell 不会将空参数传递给外部程序(自 Windows PowerShell 5.1 / PowerShell [Core] 7.0 起仍然适用,并且可能永远不会更改以保持向后兼容性)。
例如,foo.exe ""
意外导致刚刚foo.exe
被调用。
这个问题同样会影响基于数组的 splatting,因此会
$cmdArgs = "", "other"; foo.exe $cmdArgs
导致foo.exe other
而不是预期的foo.exe "" other
.
可选使用@
基于数组的喷溅:
您也可以将@
sigil 与arrays一起使用,所以这也可以:
junit-merge @command
然而,有一个微妙的区别。
虽然它在实践中很少有关系,
但更安全的选择是使用$
,因为它可以防止(尽管假设的)意外误解--%
您打算成为文字的数组元素。
只有@
语法将数组元素识别--%
为特殊的停止解析符号,--%
所述符号告诉 PowerShell 不要像通常那样解析剩余的参数,而是按原样传递它们 - 未扩展,除了扩展cmd.exe
- 样式的变量引用,例如%USERNAME%
.
这通常仅在不使用 splatting 时才有用,通常是在能够按cmd.exe
原样使用从 PowerShell 编写的命令行的上下文中,而不必考虑 PowerShell 的语法差异。
然而,在splatting的上下文中,导致的行为--%
是不明显的,最好避免:
$command
[1] 您的调用意味着将变量的值作为单个参数传递的意图,因此当 PowerShell 在幕后构建命令行时,它会双引号a.xml b.xml c.xml
包含其中包含的逐字字符串$command
以确保这一点。请注意,这些双引号与您最初将值分配给$command
. "
不幸的是,对于带有嵌入字符的值,这种自动引用被破坏了。- 例如,请参阅此答案。
[2] 作为对类 POSIX shell 的致敬,PowerShell确实执行了一种 shell 扩展,但 (a) 仅在类 Unix 平台(macOS、Linux)和 (b) 仅在调用外部程序时:不带引号的通配符模式,例如*.txt
当您调用外部程序(例如)时, as确实会扩展为它们匹配的文件名/bin/echo *.txt
,这是 PowerShell 调用本机 globbing的功能。