为什么 PowerShell 无法识别带引号的参数?

Why does PowerShell not recognize quoted parameters?

当您直接(在 PowerShell 控制台或 ISE 中)调用脚本或通过另一个 PowerShell 实例调用脚本时,为什么 PowerShell 会以不同方式处理引用的参数?

这是脚本 (TestQuotes.ps1):

param
(
    [string]
    $Config = $null
)

"Config = $Config"

结果如下:

PS D:\Scripts> .\TestQuotes.ps1 -Config "A B C"
Config = A B C
PS D:\Scripts> PowerShell .\TestQuotes.ps1 -Config "A B C"
Config = A
PS D:\Scripts> .\TestQuotes.ps1 -Config 'A B C'
Config = A B C
PS D:\Scripts> PowerShell .\TestQuotes.ps1 -Config 'A B C'
Config = A

有什么想法吗?

根据 PowerShell.exe Command-Line Helppowershell 可执行文件的第一个参数是 -Command:

PowerShell[.exe]
       [-Command { - | <script-block> [-args <arg-array>]
                     | <string> [<CommandParameters>] } ]
       [-EncodedCommand <Base64EncodedCommand>]
       [-ExecutionPolicy <ExecutionPolicy>]
       [-File <FilePath> [<Args>]]
       [-InputFormat {Text | XML}]
       [-Mta]
       [-NoExit]
       [-NoLogo]
       [-NonInteractive]
       [-NoProfile]
       [-OutputFormat {Text | XML}]
       [-PSConsoleFile <FilePath> | -Version <PowerShell version>]
       [-Sta]
       [-WindowStyle <style>]

PowerShell[.exe] -Help | -? | /?

Any text after -Command is sent as a single command line to PowerShell.

...

When the value of -Command is a string, Command must be the last parameter specified because any characters typed after the command are interpreted as the command arguments.

使用 echoargs:

很容易检查子 PowerShell 实例实际接收到的内容
PS > echoargs .\TestQuotes.ps1 -Config "A B C"
Arg 0 is <.\TestQuotes.ps1>
Arg 1 is <-Config>
Arg 2 is <A B C>

子实例进一步解析为:

'.\TestQuotes.ps1' '-Config' 'A' 'B' 'C'

这就是您获得 "wrong" 结果的地方:Config = A

如果您指定 -File 参数,您将获得所需的结果:

PS >  PowerShell -File .\TestQuotes.ps1 -Config 'A B C'
Config = A B C

PS >  PowerShell -Command .\TestQuotes.ps1 -Config 'A B C'
Config = A

tl;dr

如果您从 PowerShell 调用另一个 PowerShell 实例 ,请使用 脚本块{ ... }) 以获得可预测的行为:

Windows PowerShell:

powershell.exe { .\TestQuotes.ps1 -Config "A B C" }

PowerShell 核心:

pwsh { .\TestQuotes.ps1 -Config "A B C" }

将使参数引用按预期工作 - 它甚至 return objects 与 near-type调用的保真度,因为使用了类似于用于 PowerShell 远程处理的序列化。

但是请注意,当从 PowerShell外部 调用时,这不是一个选项],例如来自 cmd.exebash.

继续阅读,了解您在缺少 脚本块时看到的行为的解释。


PowerShell CLI(调用powershell.exe(WindowsPowerShell)/pwsh.exe(PowerShell Core) 仅支持 one 接受 positional argument 的参数(即,前面没有参数名称 例如-Command).

  • Windows PowerShell 中, 那个(隐含的)参数是 -Command

  • PowerShell Core中是-File.

    • 必须更改默认设置以支持在 Unix 中使用 CLI

第一个位置参数之后的任何参数,如果有的话,都被认为是:

  • in Windows PowerShell:PowerShell 源代码片段的一部分 传递给(隐含的)
    -Command 参数.

  • 在 PowerShell Core 中:单个参数 作为文字传递第一个位置参数(隐含的-File参数)中指定的脚本文件


传递给 -Command 的参数 - 无论是隐式还是显式 - 由 PowerShell 进行 轮解析 ,这可以是棘手:

  • 第一轮中,"..."(双引号)包围的单个参数被剥离 .

    • 如果您从 PowerShell 调用 ,这甚至适用于 最初 '...'-封闭的参数(单个-quoted),因为在幕后,PowerShell 重新引用 这样的参数,以便在调用 外部程序 时使用 "..."(包括PowerShell CLI 本身)。
  • 第二轮中,剥离的参数与空格连接形成单个字符串 then 解释为 PowerShell 源代码.


应用于您的调用,这意味着 PowerShell .\TestQuotes.ps1 -Config "A B C"PowerShell .\TestQuotes.ps1 -Config 'A B C' 导致 PowerShell 最终解析并执行以下代码:

.\TestQuotes.ps1 -Config A B C

由于经过2轮解析,原来的引用被丢失了,导致三个通过了不同的参数,这解释了你的症状。


如果您必须使命令工作而无需脚本块,您有两个选择:

  • 使用-File只适用一个轮解析:

    powershell.exe -File .\TestQuotes.ps1 -Config "A B C"
    
    • 也就是说,除了剥离封闭的 "..." 之外,生成的参数被视为 文字 - 然而, 通常 您想要什么。
  • 使用(隐含)-Command,应用额外的引用层:

    powershell.exe -Command .\TestQuotes.ps1 -Config '\"A B C\"'
    

请注意 PowerShell 如何需要 " 个字符。在传递给 到其 CLI 的参数中作为 \" 进行转义,而 在 PowerShell 中 `"(或 "") 必须使用。