【问题标题】:Use a variable in PowerShell to pass multiple arguments to an external program在 PowerShell 中使用变量将多个参数传递给外部程序
【发布时间】:2020-03-22 23:49:50
【问题描述】:

我下载了用于合并 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

这不起作用。错误是

错误:找不到文件

有没有人遇到过类似的问题?

【问题讨论】:

  • 改用$command = "a.xml", "b.xml", "c.xml"

标签: powershell junit parameter-passing parameter-splatting


【解决方案1】:

我遇到了类似的问题。 powershell 中的这种技术对我有用:

Invoke-Expression "junit-merge $command"

我还尝试了以下方法(来自 powershell 脚本)并且它有效:

cmd / c "junit-merge $command"

【讨论】:

    【解决方案2】:
    # 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 expansions 之一)的影响,并且确实会导致 3 个不同的参数(尽管即使存在基于数组的调用也会更好) .
    PowerShell 不支持类似bash 的外壳扩展[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 技术的应用程序,您可以在其中指定要通过变量传递给命令的参数:

    • 要么(通常仅用于外部程序,如您的情况):

    • 作为一个数组作为位置参数单独传递的参数,如上所示。

    • 或者(更常见的是在调用 PowerShell 命令时):

    • 作为哈希表来传递命名参数值,您必须在其中替换变量引用中的$ sigil @;例如,在你的情况下@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

    此问题同样会影响基于数组的 splatting,因此
    $cmdArgs = "", "other"; foo.exe $cmdArgs 会产生 foo.exe other 而不是预期的 foo.exe "" other


    可选使用 @ 和基于数组的 splatting

    您也可以将@ 印记与数组一起使用,所以这也可以:

    junit-merge @command
    

    但是有一个微妙的区别。

    虽然在实践中很少有影响, 更安全的选择是使用$,因为它可以防止(无论假设如何)意外误解您打算成为一个文字--% 数组元素。

    只有@ 语法将数组元素--% 识别为特殊的stop-parsing symbol, --%

    该符号告诉 PowerShell 不要像通常那样解析剩余的参数,而是按原样传递它们 - 未扩展,除了扩展 cmd.exe 样式的变量引用,例如 %USERNAME%

    这通常仅在使用 splatting 时有用,通常是在能够按原样使用 PowerShell 为 cmd.exe 编写的命令行的上下文中,而无需考虑 PowerShell 的语法差异。

    然而,在 splatting 的上下文中,--% 导致的行为并不明显,最好避免:

    • 与直接参数传递一样,--% 已从生成的命令行中删除

    • 参数边界丢失,因此通常在命令行上放置为"foo bar" 的单个数组元素"foo bar" 放置为foo bar,即有效地作为2 个参数。


    [1] 您的调用意味着将变量 $command 的值作为 单个参数 传递的意图,因此当 PowerShell 在后台构建命令行时,它会使用双引号$command 中包含的逐字 a.xml b.xml c.xml 字符串以确保这一点。请注意,这些双引号与您最初为$command 分配值的方式无关。 不幸的是,对于嵌入了 " 字符的值,这种自动引用被破坏了。 - 例如,参见this answer

    [2] 作为对类 POSIX shell 的致敬,PowerShell 确实 执行一种 shell 扩展,但 (a) 仅在类 Unix 平台(macOS、Linux)和(b) 仅在调用外部程序时:当您调用外部程序(例如,/bin/echo *.txt)时,不带引号的通配符模式(例如 *.txt)确实会扩展为它们的匹配文件名,这是 PowerShell 调用 native globbing.

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-09-22
      • 1970-01-01
      • 2014-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多