我正在尝试在 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'
由于 & 字符用于启动新命令,因此它会破坏 url 并想要执行其余部分。
有没有办法告诉 powershell 不要这样做?
Olaf的回答提供了有效的解决方案;让我添加一个解释:
问题的根源是两种行为的汇合:
调用外部程序时,PowerShell 仅根据给定参数值包含空格对每个参数执行按需双引号 - 否则,参数将被传递不加引号 - 无论是否包含空格该值是最初在PowerShell命令中引用的(例如,
cmd /c echo ab
、cmd /c echo 'ab'
和 cmd /c echo "ab"
都会导致 unquoted ab
作为命令行上的最后一个标记传递(PowerShell 在幕后重建以最终用于执行)。
Azure
az
CLI 作为批处理文件实现 (az.cmd
),当调用批处理文件时,cmd.exe
会解析给定的参数;令人惊讶的是 - 并且可以说是不恰当的 - 它解析它们就好像命令是从cmd.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.
共有三种解决方法:
"..."
引用:# 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.
# 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"
cmd /c
调用并传递包含批处理文件调用及其所有参数的 single 字符串,(最终)使用 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.
退一步:
现在,如果您不必担心所有这些事情,那不是很好吗? 特别是因为您可能不知道或不关心给定的 CLI - 例如
az
- 是否会 发生 实现 作为批处理文件?
作为 shell,PowerShell 应尽最大努力在幕后忠实地传递参数,并允许调用者专门专注于仅满足 PowerShell 的语法规则:
不幸的是,PowerShell 迄今为止(PowerShell 7.2)通常在这方面做得非常差,无论
cmd.exe
有什么怪癖 - 请参阅这个答案了解总结。
关于
cmd.exe
的(批处理文件调用)怪癖,PowerShell 可以在未来版本中可以预见地弥补它们 - 但不幸的是,这似乎不会发生;请参阅 GitHub 问题 #15143。
我现在无法访问 Azure 租户进行测试,实际上我一般没有使用 Azure CLI 的经验,但我希望它能正常工作:
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'
或者这个:
az rest --method GET --headers "Content-Type=application/json" `
--% --uri "https://graph.microsoft.com/v1.0/groups?$count=true&$filter=startsWith(displayName,some+filter+text)&$select=id,displayName"
我添加反引号只是为了提高可读性 - 您可以在实际代码中删除它们。
虽然 Olaf 的解决方案和 mklement0 的解决方法有效,但还有一些替代解决方案选项,请参阅 Microsoft 的示例。好处是,如果您有一个包含&符号的变量,它们也可以工作。
# When quoted by single quotes ('), double quotes (") are preserved by PowerShell and sent
# to Command Prompt, so that ampersand (&) is treated as a literal character
> az '"a&b"' --debug
Command arguments: ['a&b', '--debug']
# Escape double quotes (") with backticks (`) as required by PowerShell
> az "`"a&b`"" --debug
Command arguments: ['a&b', '--debug']
# Escape double quotes (") by repeating them
> az """a&b""" --debug
Command arguments: ['a&b', '--debug']
# With a whitespace in the argument, double quotes (") are preserved by PowerShell and
# sent to Command Prompt
> az "a&b " --debug
Command arguments: ['a&b ', '--debug']
# Use --% to stop PowerShell from parsing the argument
> az --% "a&b" --debug
Command arguments: ['a&b', '--debug']
以变量为例:
$ihaveweirdchars="A@J7aa6bkK&mtKK&53F9`$jC0P`$0Khi"
az "`"$ihaveweirdchars`"" --debug
Command arguments: ['A@J7aa6bkK&mtKK&53F9$jC0P$0Khi', '--debug']