PowerShell:转义 CLI 调用的规则

PowerShell: escaping rules for CLI calls

提问人:antonio 提问时间:3/23/2022 更新时间:11/16/2022 访问量:838

问:

正如我所了解的,当使用 该选项从调用 PowerShell 时,转义规则本质上是 Linux 中使用的规则。因此,您使用反斜杠 (),而不是通常的反引号 ()。cmd.exe-Command\`

这是说你不写:

C:\> powershell -c "echo `"That's a single quote: ' `""

C:\> powershell -c "echo \"That's a single quote: ' \""

要获得:

That's a single quote: '

这与您在 Linux 终端中键入的内容完全相同:

~ >>> bash -c "echo \"That's a single quote: ' \""                                                                      

如果我没记错的话,此功能名为 PSNativeCommandArgumentPassing。 但比较在一定程度上是有效的。事实上,在 Linux 下,您写道:

~ >>> bash -c "echo \"That's a double quote: \\\" \""                                                              

要获得:

That's a double quote: " 

而等效的 PowerShell 行:

C:\> powershell -c "echo \"That's a double quote: \\\"  \""

The string is missing the terminator: ".

通过反复试验,我意识到:

C:\> powershell -c "echo \"That's a double-quote: `""  \""

按预期工作。

你能向我解释一下背后的逻辑是什么吗?`""

另外,从 而不是 调用 PowerShell 时,有哪些等效命令?powershell.execmd.exe

PowerShell 转义 引号

评论


答:

3赞 mklement0 3/23/2022 #1

TL的;博士

  • 仅在 PowerShell 的命令行上,用于传递 () 应执行的代码。\""-c-Command

    • 只有在生成的代码中,PowerShell 通常的转义 as 才适用,因此你可能必须结合使用这两种转义技术:."`"`\"

      • 您的尝试 ,具有相同的效果,但效果不佳[1]`""
    • \"在 PowerShell 自己对其命令行的分析方面可以可靠地工作,但可能会在 cmd 端.exe情况下中断调用 - 有关解决方法,请参阅底部。

  • 从 PowerShell 调用 CLI(外部控制台应用程序)时,不仅 PowerShell 自己的引用规则首先应用不同的引用规则(支持字符串,嵌入在 Escaped as 中),PowerShell 7.2.x 之前的一个长期存在的错误还需要转义字符。 with 嵌入到外部程序参数中时(仅限);请看这个答案'...'""..."`""\


此功能名为PSNativeCommandArgumentPassing

洛伊此功能 - 在 PowerShell 7.3 中成为官方功能(请参阅此答案) - 不会发挥作用,因为:

  • 它通常不可用(也不会)在通过其 CLI 调用的旧版 Windows PowerShell 版本中.exe而新式跨平台 PowerShell (Core) 版本的 CLI 是 pwsh.exe )

  • 它仅适用于来自 PowerShell 会话内部的调用。

  • 它旨在解决 PowerShell 调用具有嵌入或空字符串字符串参数的外部程序时长期存在的问题 - 请参阅此答案"

    • 因此,链接的答案解决了您的最后一个问题

      • 另外,从 而不是 调用 PowerShell 时,有哪些等效命令?powershell.execmd.exe

    • 简而言之:不幸的是,至少在 PowerShell 7.2.x 之前,您必须另外手动转义字符。""


为什么以下工作原理:cmd.exe

powershell -c "echo \"That's a single quote: ' \""
  • PowerShell 仅在其命令行上\ 识别为转义字符,以便与其他 CLI 保持一致。

    • PowerShell 会话中,只有 ' (backtick) 用作转义字符。

    • 注意:虽然 \“转义 PowerShell(命令行解析)端的两个 PowerShell 版本中始终如一地工作,但在某些情况下,它可能会破坏 cmd.exe 自己的解析 - 请参阅底部。

  • 当 PowerShell CLI 通过 () 调用并带有一段要执行的 PowerShell 源代码时,该代码(以下参数)分两个阶段进行分析:-c-Command-c

    • 首先,所有转义的字符都被剥离转义的字符 () 被保留和未转义。"\"
    • 只有这样,才会分析结果并作为 PowerShell 代码执行。

因此,PowerShell 最终执行的是逐字执行的:

echo "That's a single quote: ' "

从上面可以看出为什么这不起作用

:: SYNTAX ERROR
powershell -c "echo \"That's a double quote: \\\"  \""

PowerShell 最终尝试逐字执行

echo "That's a double quote: \" "

这是一个语法错误,因为在 PowerShell 会话内部不会转义 -only 或 - 内部 ,或者 - do。\"`""..."""


从上面可以看出为什么这(大部分)有效:

:: Works, but not robustly
powershell -nop -c "echo \"That's a double-quote: `""  \""
  • "" 有时,但并不总是可以替代;[1] 这里确实如此 - 另请参阅底部。\"

  • 因此,PowerShell 最终会逐字执行以下操作,这是有效的,因为传递的转义现在是 -escaped,因为它需要位于 PowerShell 字符串中:"`"..."

     echo "That's a double-quote: `"  "
    

为了避免脆弱的逃逸,最好通过将所需的 -escaping 与命令行 -escaping - 相结合来制定此命令,即 - 但请参阅底部,了解完全健壮的解决方案:""`\`\"

powershell -nop -c "echo \"That's a double-quote: `\"  \""

避免 cmd的解析问题.exe是 \“ 的安全替代方案:

虽然 \“ 转义 PowerShell(命令行解析)端的两个 PowerShell 版本中始终如一地工作,但在某些情况下,它可能会破坏 cmd.exe 自己的解析。相比之下,从无 shell 环境(如任务计划程序)调用时,使用是安全的。\"

虽然有一些解决方法,但不幸的是,它们是特定于 PowerShell 版本的:

  • Windows PowerShell (powershell.exe)

    • 使用 (原文如此) 而不是"^""\"
  • PowerShell (Core) (v6+,pwsh.exe)

    • 使用代替""\"

重要:

  • 这些变通办法要求将传递给 () 的整个代码作为单个“...”括起来的参数传递-c-Command

  • -c (-Command) 还接受多个参数 - 可以单独使用双引号,也可以不使用双引号 - 在这种情况下,它只是在剥离未转义后将这些参数连接起来,以形成要执行的代码。这种技术在情境上允许你摆脱 -逃义(例如,,但 (a) 它要求你密切注意命令的哪些部分将被视为未加引号,(b) 需要你 -转义任何元字符,例如在这些部分中,以及 (c) 总是执行空格规范化,即将多个空格的运行折叠成一个空格。"\"powershell -c \"Between 2 & 3`\"\"cmd.exe^cmd.exe&

以下旨在逐字打印的调用演示了这一点: Between 2 & 3" 

:: BREAKS, because cmd.exe sees `&` as *outside a double-quoted string*
powershell -c " \" Between 2 & 3`\" \" "

:: PowerShell (Core) workaround
pwsh -c " "" Between 2 & 3`"" "" "

:: Windows PowerShell workaround
powershell -c " "^"" Between 2 & 3`"^"" "^"" "

[1] “...”里面的“”不起作用的一个例子是powershell -c “echo \” Nat '“”King'“” Cole \“”:而不是Nat King” Cole,它打印Nat “King Cole,即第二个转义丢失(但是,它在pwsh.exe中可以正常工作,如底部所述)。最终不值得推测 powershell.exe -c 如何准确地解析嵌入的 “” 序列,因为它显然不可靠,并且确实存在可靠的替代方案(\“,或者,来自 cmd.exe,也是”^“”)。

评论

0赞 antonio 3/25/2022
我正在替换反勾。如果外部引号被剥离并且内部保持未转义(如在 Bash 中),则传递给 PowerShell 的内容应该是 ,但这不能用作 PS 代码。!powershell -c "echo \"That's a double-quote: !"" \""echo "That's a double-quote: !"" "
0赞 mklement0 3/25/2022
@antonio,如答案中所解释的那样,在情境中保留为 ,但是,由于它不能可靠地工作,因此应避免使用(除非在 中可以安全使用)。在您的示例中看到的是"""powershell.exepwsh.exepowershell.exeecho "That's a double quote: `" "
0赞 mklement0 3/25/2022
@antonio,如果你想要一个 -escaping 崩溃的例子,试着 - 你会看到第二个 escape 丢失了;相比之下,它工作正常并打印所需的内容""powershell -c "echo \" Nat `""King`"" Cole \"""pwsh.exeNat "King" Cole
0赞 mklement0 3/25/2022
@antonio 最终不值得推测嵌入序列是如何被 解析的,因为它显然不可靠,并且确实存在可靠的替代方案( 或者,从 ,也""powershell.exe -c\"cmd.exe"^"")