Powershell 7:在字符串文字中使用与号 (&)

ver*_*edr 6 powershell

我正在尝试在 PowerShell 中执行以下命令,但我不知道如何转义作为 URL 一部分的 & 符号

  az rest `
    --method GET `
    --uri ("https://graph.microsoft.com/v1.0/groups?`$count=true&`$filter=startsWith(displayName,'some+filter+text')&`$select=id,displayName") `
    --headers 'Content-Type=application/json'
Run Code Online (Sandbox Code Playgroud)

由于 & 字符用于启动新命令,因此它会破坏 url 并希望执行其余部分。

有没有办法告诉 powershell 不要这样做?

mkl*_*nt0 10

奥拉夫的回答提供了一个有效的解决方案;让我添加一个解释:

问题的根源是两种行为的融合:

  • 调用外部程序时,PowerShell根据给定参数值是否包含空格来按需对每个参数执行双引号- 否则,参数将不带引号传递- 无论该值最初是否在PowerShell命令中被引用(例如cmd /c echo abcmd /c echo 'ab'、 和cmd /c echo "ab"所有结果都会导致不带引号的 ab情况作为命令行上的最后一个标记传递(PowerShell 在幕后重建以最终用于执行)。

  • Azure CLI作为批处理文件( )az实现,当调用批处理文件时,它会解析给定的参数;令人惊讶的是——而且可以说是不恰当的——它解析它们,就好像命令是从会话内部提交的一样。az.cmdcmd.execmd.exe

因此,如果从 PowerShell 传递到批处理文件的参数(a) 不包含空格,但 (b) 包含cmd.exe元字符(例如&),则调用会中断

一个简单的演示,使用cmd /c echo调用作为对批处理文件的调用的替代:

# !! Breaks, because PowerShell (justifiably) passes *unquoted* a&b
# !! when it rebuilds the command line to invoke behind the scenes.
PS> cmd /c echo 'a&b'
a
'b' is not recognized as an internal or external command,
operable program or batch file.
Run Code Online (Sandbox Code Playgroud)

有以下三种解决方法

  • 使用嵌入 "..."引用:
# OK, but with a CAVEAT: 
#   Works as of PowerShell 7.2, but arguably *shouldn't*, because
#   PowerShell should automatically *escape* the embedded " chars. as ""
PS> cmd /c echo '"a&b"'
"a&b"

# Ditto, using an *expandable* (interpolating) PowerShell string:
PS> cmd /c echo "`"$HOME & Family; can't put a `$ value on that.`""
"C:\Users\jdoe & Family; can't put a $ value on that." # e.g. 
Run Code Online (Sandbox Code Playgroud)
# OK, but with a CAVEAT:
#  Requires "..." quoting, but doesn't recognize *PowerShell* variables,
#  also doesn't support single-quoting and line continuation.
PS> cmd /c echo --% "a&b"
 "a&b"
Run Code Online (Sandbox Code Playgroud)
  • 调用 viacmd /c并传递包含批处理文件调用及其所有参数的单个字符串,(最终)使用cmd.exe的 语法
# OK (remember, cmd /c echo stands for a call to a batch file, such as az.cmd)
# Inside the single string passed to the outer cmd /c call,
# be sure to use "...", as that is the only quoting cmd.exe understands.
PS> cmd /c 'cmd /c echo "a&b"'
"a&b"

# Ditto, using an *expandable* (interpolating) PowerShell string:
PS> cmd /c "cmd /c echo `"$HOME & Family; can't put a `$ value on that.`""
"C:\Users\jdoe & Family; can't put a $ value on that." # e.g. 
Run Code Online (Sandbox Code Playgroud)

退一步说

现在,如果您不必担心所有这些事情,那不是很好吗?特别是因为您可能不知道或不关心给定的 CLI(例如)是否az恰好作为批处理文件实现?

作为 shell,PowerShell 应尽最大努力在幕后忠实地传递参数,并允许调用者专注仅满足PowerShell 的语法规则:

  • 不幸的是,迄今为止,PowerShell(PowerShell 7.2)在这方面通常做得很差,无论cmd.exe其怪癖如何 - 请参阅此答案以获取摘要。

  • 对于cmd.exe(批处理文件调用)的怪癖,PowerShell可以预见地在未来版本中对其进行补偿 - 但不幸的是,这似乎不会发生;请参阅GitHub 问题 #15143