在 PowerShell 中使用变量将多个参数传递给外部程序
Use a variable in PowerShell to pass multiple arguments to an external program
我下载了用于合并 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
这不起作用。错误是
Error: File not found
有没有人遇到过类似的问题?
# 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-像shell如bash
那样:在bash
,变量 $command
的值 - 由于被引用 未引用 - 将受到 分词 (其中之一 -称为 shell expansions) 并且确实会产生 3 个不同的参数(尽管即使有基于数组的调用也是可取的)。
PowerShell 不支持 bash
-like shell 扩展[2];它具有不同的、通常更灵活的构造,例如下面讨论的溅射技术。
相反,将您的参数定义为数组的单个 元素,正如 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 技术的应用,您可以在其中指定要通过 变量[=162] 传递给命令的参数=]:
两者之一(通常仅用于外部程序,如您的情况):
作为一个 数组 参数作为 位置参数单独传递 ,如上图
或者(在调用 PowerShell 命令 时更常见):
作为hashtable传递named参数值,在您必须 将变量引用中的 $
标记替换为 @
;例如,在您的情况下 @command
;例如,以下等效于调用 Get-ChildItem C:\ -Directory
:
$paramVals = @{ LiteralPath = 'C:\'; Directory = $true }; Get-ChildItem @paramVals
警告基于数组的展开:
由于 this GitHub issue 中详述的错误,PowerShell 不会将 空 参数传递给外部程序(仍然适用于 Windows PowerShell 5.1 / PowerShell [Core] 7.0,并且可能永远不会更改以保持向后兼容性)。
例如,foo.exe ""
意外导致仅 foo.exe
被调用。
这个问题同样会影响基于数组的展开,因此
$cmdArgs = "", "other"; foo.exe $cmdArgs
结果是 foo.exe other
而不是预期的 foo.exe "" other
.
可选择使用 @
和基于数组的展开:
您也可以将 @
印记与 数组 一起使用,所以这也可以工作:
junit-merge @command
然而,有一个微妙的区别。
虽然在实践中很少有影响,
更安全的选择是使用 $
,因为它可以防止(尽管是假设的)意外误解您打算成为 [=110] 的 --%
数组元素=]文字.
只有 @
语法将数组元素 --%
识别为特殊的 stop-parsing symbol, --%
所述符号告诉 PowerShell 不要像通常那样解析剩余的参数,而是按原样传递它们 - 未扩展,除了扩展 cmd.exe
样式的变量引用,例如 %USERNAME%
。
这通常仅在 不 使用 splatting 时有用,通常是在能够使用从 PowerShell 原样为 cmd.exe
编写的命令行的情况下,无需考虑 PowerShell 的语法差异。
然而,在 splatting 的上下文中,--%
导致的行为并不明显,最好避免:
与直接参数传递一样,--%
从生成的命令行中 删除。
参数边界丢失,因此单个数组元素foo bar
,通常在命令中放置为"foo bar"
行,被放置为 foo bar
,即有效地作为 2 个参数。
[1] 您的调用暗示了将变量 $command
的值作为 单个参数 传递的意图,因此当 PowerShell 构建命令时在幕后,它逐字双引号 a.xml b.xml c.xml
包含在 $command
中的字符串以确保这一点。请注意,这些双引号与您最初为 $command
赋值的方式无关。
不幸的是,对于嵌入 "
个字符的值,这种自动引用被破坏了。 - 例如参见 [=59=]。
[2] 作为对 POSIX-like shells 的点头,PowerShell 确实 执行一种 shell 扩展,但 (a) 仅在类 Unix 平台上(macOS,Linux)和 (b) 仅在调用外部程序时:不带引号的通配符模式如 *.txt
确实被扩展为其匹配的文件名当您调用外部程序(例如 /bin/echo *.txt
)时,这是 PowerShell 调用 native globbing.
的功能
我遇到了类似的问题。 powershell 的这项技术对我有用:
Invoke-Expression "junit-merge $command"
我还尝试了以下方法(来自 powershell 脚本)并且有效:
cmd / c "junit-merge $command"
我下载了用于合并 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
这不起作用。错误是
Error: File not found
有没有人遇到过类似的问题?
# 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-像shell如bash
那样:在bash
,变量 $command
的值 - 由于被引用 未引用 - 将受到 分词 (其中之一 -称为 shell expansions) 并且确实会产生 3 个不同的参数(尽管即使有基于数组的调用也是可取的)。
PowerShell 不支持 bash
-like shell 扩展[2];它具有不同的、通常更灵活的构造,例如下面讨论的溅射技术。
相反,将您的参数定义为数组的单个 元素,正如 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 技术的应用,您可以在其中指定要通过 变量[=162] 传递给命令的参数=]:
两者之一(通常仅用于外部程序,如您的情况):
作为一个 数组 参数作为 位置参数单独传递 ,如上图
或者(在调用 PowerShell 命令 时更常见):
作为hashtable传递named参数值,在您必须 将变量引用中的
$
标记替换为@
;例如,在您的情况下@command
;例如,以下等效于调用Get-ChildItem C:\ -Directory
:$paramVals = @{ LiteralPath = 'C:\'; Directory = $true }; Get-ChildItem @paramVals
警告基于数组的展开:
由于 this GitHub issue 中详述的错误,PowerShell 不会将 空 参数传递给外部程序(仍然适用于 Windows PowerShell 5.1 / PowerShell [Core] 7.0,并且可能永远不会更改以保持向后兼容性)。
例如,foo.exe ""
意外导致仅 foo.exe
被调用。
这个问题同样会影响基于数组的展开,因此
$cmdArgs = "", "other"; foo.exe $cmdArgs
结果是 foo.exe other
而不是预期的 foo.exe "" other
.
可选择使用 @
和基于数组的展开:
您也可以将 @
印记与 数组 一起使用,所以这也可以工作:
junit-merge @command
然而,有一个微妙的区别。
虽然在实践中很少有影响,
更安全的选择是使用 $
,因为它可以防止(尽管是假设的)意外误解您打算成为 [=110] 的 --%
数组元素=]文字.
只有 @
语法将数组元素 --%
识别为特殊的 stop-parsing symbol, --%
所述符号告诉 PowerShell 不要像通常那样解析剩余的参数,而是按原样传递它们 - 未扩展,除了扩展 cmd.exe
样式的变量引用,例如 %USERNAME%
。
这通常仅在 不 使用 splatting 时有用,通常是在能够使用从 PowerShell 原样为 cmd.exe
编写的命令行的情况下,无需考虑 PowerShell 的语法差异。
然而,在 splatting 的上下文中,--%
导致的行为并不明显,最好避免:
与直接参数传递一样,
--%
从生成的命令行中 删除。参数边界丢失,因此单个数组元素
foo bar
,通常在命令中放置为"foo bar"
行,被放置为foo bar
,即有效地作为 2 个参数。
[1] 您的调用暗示了将变量 $command
的值作为 单个参数 传递的意图,因此当 PowerShell 构建命令时在幕后,它逐字双引号 a.xml b.xml c.xml
包含在 $command
中的字符串以确保这一点。请注意,这些双引号与您最初为 $command
赋值的方式无关。
不幸的是,对于嵌入 "
个字符的值,这种自动引用被破坏了。 - 例如参见 [=59=]。
[2] 作为对 POSIX-like shells 的点头,PowerShell 确实 执行一种 shell 扩展,但 (a) 仅在类 Unix 平台上(macOS,Linux)和 (b) 仅在调用外部程序时:不带引号的通配符模式如 *.txt
确实被扩展为其匹配的文件名当您调用外部程序(例如 /bin/echo *.txt
)时,这是 PowerShell 调用 native globbing.
我遇到了类似的问题。 powershell 的这项技术对我有用:
Invoke-Expression "junit-merge $command"
我还尝试了以下方法(来自 powershell 脚本)并且有效:
cmd / c "junit-merge $command"