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

时间:2019-03-16 21:45:55

标签: windows powershell parameters quotes

为什么直接调用脚本(在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

有什么想法吗?

2 个答案:

答案 0 :(得分:6)

根据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 | -? | /?
  

-Command之后的任何文本均作为单个命令行发送到PowerShell。

     

...

     

-Command的值是一个字符串时,Command必须是指定的最后一个参数,因为在命令后键入的任何字符都将解释为命令参数

使用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'

这是您得到“错误”结果的地方: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

答案 1 :(得分:4)

tl; dr

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

Windows PowerShell:

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

PowerShell 核心

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

将使参数的引用按预期工作-甚至从调用中返回具有近似保真度的 objects ,因为序列化与用于使用了PowerShell远程处理。

但是请注意,从PowerShell的外部 (例如cmd.exe )调用时,这不是 不是 bash

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


PowerShell CLI (调用powershell.exe(Windows PowerShell)/ pwsh.exe(PowerShell Core ))仅支持一个参数,该参数接受 positional 参数(即,没有以参数名称开头的值,例如-Command)。

  • Windows PowerShell中,(隐含)参数为 -Command

  • PowerShell Core 中,它是 -File

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

第一个位置参数(如果有)后的 中的任何参数将被考虑:

  • 在Windows PowerShell中: PowerShell源代码片段的一部分传递给了(暗示)
    -Command参数。

  • 在PowerShell Core
  • 各个参数传递给作为文字传递给脚本文件 第一个位置参数(隐含的-File参数)。


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

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

    • 如果您是从PowerShell 调用,则此方法甚至适用于最初由 '...'括起来(单引号)的参数,因为场景,PowerShell 重新引用这样的参数,以便在调用外部程序(包括PowerShell CLI本身)时使用"..."
  • 第二回合中,将剥离的参数加上空格,形成单个字符串,然后 解释为 PowerShell源代码


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

.\TestQuotes.ps1 -Config A B C

也就是说,由于进行了两轮解析,原始报价被丢失,导致传递了三个个不同的参数,解释您的症状。


如果必须在没有脚本块的情况下使命令工作 ,则有两个选择:

  • 使用-File 仅应用一个一轮解析

    powershell.exe -File .\TestQuotes.ps1 -Config "A B C"
    
    • 也就是说,除了剥离封闭的"..."之外,生成的参数还被视为 literals -然而,通常是 您想要的。
  • 使用(暗示)-Command,应用额外的报价层

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

请注意PowerShell如何要求"个字符。要在传递给其CLI的参数中以\"的形式转义,而在PowerShell中的 中必须使用`"(或""