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

v1uwarro  于 2022-11-10  发布在  Shell
关注(0)|答案(2)|浏览(199)

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

有什么主意吗?

waxmsbnn

waxmsbnn1#

根据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
tsm1rwdh

tsm1rwdh2#

tl;dr

如果您要**从PowerShell调用另一个PowerShell示例,请使用脚本块({ ... })**来获得可预测的行为:
Windows PowerShell:

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

PowerShell酷睿

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

将使参数引用按预期工作--它甚至将从调用中以接近类型的保真度返回对象,因为使用了类似于PowerShell远程处理的序列化。
但是,请注意,当从PowerShell的外部**调用时,例如从cmd.exebash,这不是*选项。
请继续阅读,了解在没有脚本块的情况下看到的行为的解释。

PowerShell CLI(调用powershell.exe(Windows PowerShell)/pwsh.exe(PowerShellCore)仅支持一个接受位置*参数参数(即前面没有参数名称*的值,如-Command))。

  • Windows PowerShell中,该(隐含)参数为-Command
  • PowerShell*核心中,为**-File**。
  • 必须更改默认设置以支持在Unix shebang lines中使用CLI。
  • 第一个位置参数(如果有)之后的任何参数都将被考虑:
  • 在Windows PowerShell中:部分PowerShell源代码传递给(隐含)

-Command参数。

  • in PowerShellCore单独参数作为文字传递给第一个位置参数(隐含的-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"
  • 也就是说,**除了去掉封闭的"..."之外,生成的参数被视为文字--然而,这通常是您想要的。
  • 使用(隐含)-Command,增加引号
powershell.exe -Command .\TestQuotes.ps1 -Config "'A B C'"

外部的"..."在命令行解析期间被剥离,留下内部的单引号'A B C'字符串作为要执行的代码的一部分。
如果您还想将"用于内部引用(这里不是必需的),则必须使用来自外部PowerShell的"\"A B C\""和来自内部PowerShell的"\"A B C`""[1]-也就是说,PowerShell需要"个字符。要在*传递给其CLI*的参数中将"转义为",而在*PowerShell*中,必须使用``"(或"")。
[1]除了```之外,\的转义应该不是必须的,但不幸的是,这是由于至少是PowerShell 7.2.x的一个长期存在的错误所致,该错误可能在7.3中得到修复-请参阅this answer

相关问题