我下载了用于合并 junit 报告的 npm 包 - https://www.npmjs.com/package/junit-merge。
问题是我有多个文件要合并,并且我正在尝试使用字符串变量来保存要合并的文件名。
当我写脚本时,myslef像:
junit-merge a.xml b.xml c.xml
这有效,正在创建合并的文件,但是当我这样做时
$command = "a.xml b.xml c.xml"
junit-merge $command
这行不通。错误是
错误:找不到文件
有没有人遇到过类似的问题?
# 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那样bash
做:在bash
中,变量$command
的值 - 由于被引用不加引号- 将受到单词拆分(所谓的shell扩展之一)的影响,并且确实会产生3个不同的参数(尽管即使在那里,基于数组的调用也会更好bash
><)。;它具有不同的,通常更灵活的结构,例如下面讨论的喷溅技术。
相反,将您的参数定义为数组的各个元素,正如 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
Caveat re-based Array-splatting:
由于GitHub问题#6280中详述的错误,PowerShell不会将空参数传递给外部程序(适用于所有Windows PowerShell版本/最高为PowerShell(核心)7.2.x。此问题已在 7.3 中修复,Windows 上有选择性例外,同时修复了具有嵌入式"
的参数的传递方式 - 请参阅$PSNativeCommandArgumentPassing
首选项变量。
例如,在PowerShell v7.2.x之前,foo.exe ""
意外地导致只调用foo.exe
。
这个问题同样会影响基于数组的拼接,因此$cmdArgs = "", "other"; foo.exe $cmdArgs
会导致foo.exe other
而不是预期的foo.exe "" other
。
解决方法(如果设置了$PSNativeCommandArgumentPassing = 'Legacy'
,则也适用于 v7.3+)是使用'""'
(原文如此)。
在基于阵列的外部程序拼接中可选使用@
:
如前所述,对于外部程序,不需要使用@
代替$
,因为传递数组会隐式导致拼接。(相比之下,当调用要将数组元素作为单个位置参数传递到的PowerShell命令时,必须使用@
)
但是,您也可以选择将@
与外部程序一起使用,可以说它更清楚地传达了拼接意图:
junit-merge @command
然而,有一个微妙的行为区别 - 尽管在实践中可能很少出现:
更安全的选择是使用$
,因为它可以防止(无论假设的)对包含您打算按原样传递--%
的数组元素的意外误解。
只有@
语法将具有逐字值--%
的数组元素识别为特殊的停止分析标记,--%
所述令牌告诉PowerShell不要像往常一样解析剩余的参数,而是按原样传递它们 - 未展开,除了扩展cmd.exe
样式的变量引用,例如%USERNAME%
。
这通常仅在不使用splatting 时才有用,通常是在能够按原样使用 PowerShell 为cmd.exe
编写的命令行的上下文中,而无需考虑 PowerShell 的语法差异。
然而,在溅射的情况下,--%
导致的行为并不明显,最好避免:
与直接参数传递一样,
--%
将从生成的命令行中删除。参数边界丢失,因此通常作为
"foo bar"
放置在命令行上的单个数组元素foo bar
被放置在foo bar
,即有效地作为2个参数放置。
[1] 你的调用意味着将变量$command
的值作为单个参数传递的意图,因此当 PowerShell 在后台构建命令行时,它会将$command
中包含的逐字a.xml b.xml c.xml
字符串双引号以确保这一点。请注意,这些双引号与您最初如何为$command
分配值无关。 不幸的是,对于嵌入"
字符的值,这种自动引用被破坏了。 - 例如,请参阅此答案。
[2] 作为对类似POSIX的shell的致敬,PowerShell确实执行了一种shell扩展,但(a)仅在类Unix平台(macOS,Linux)和(b)仅在调用外部程序时:当您调用外部程序(例如/bin/echo *.txt
)时,未引号的通配符模式(例如*.txt
)确实会扩展为其匹配的文件名,这是PowerShell称为本机通配的功能。
我也有类似的问题。Powershell的这种技术对我有用:
Invoke-Expression "junit-merge $command"
我还尝试了以下内容(来自powershell脚本)并且它可以工作:
cmd / c "junit-merge $command"