在 Powershell 中转义引号

Escaping quotation marks in Powershell

在 Powershell 中,我想运行执行以下命令:

bash -c "echo 'hello world!'"

(我知道我可以直接使用echoWrite-Host,但我需要使用bash -c)并且我希望hello被单引号括起来和 world 双引号。换句话说,我想要以下输出:

'hello' "world"!

我应该转义那些引号吗?

由于您正在调用程序(例如 bash),"normal" 规则 escape quotation characters 不一定按 "same" 方式工作。不是 PowerShell 解释字符串(通常遵循 "normal" 引号规则),而是将 arguments 传递给程序。这是以完全不同的方式解释的。

TLDR:转义引号字符需要以下命令:

bash -c "echo \'Hello\' \\""World\\""!"

输出:

'Hello' "World"!

现在看起来非常复杂。那么,让我们分解一下。

首先,bash中正确的命令是什么来输出想要的报价?让我们试试常规引号:

HAL9256@HAL9000:~$ echo 'Hello' "World"!
Hello World!

没有引号。哦耶!我必须逃离他们。在 bash 中,我必须用反斜杠 (\) 来转义它们:

HAL9256@HAL9000:~$ echo \'Hello\' \"World\"!
'Hello' "World"!

好了。我们必须使用单个反斜杠来正确转义 bash 中的引号。因此,让我们将其插入 PowerShell:

PS C:\> bash -c "echo \'Hello\' \"World\"!"
/bin/bash: -c: line 0: unexpected EOF while looking for matching `"'
/bin/bash: -c: line 1: syntax error: unexpected end of file

那没用。哦对了,在 PowerShell 中我们必须用反引号 (`) 转义双引号,因为它们在一组双引号内:

PS C:\> bash -c "echo \'Hello\' \`"World\`"!"
'Hello' World!

嗯,这并没有出错,但仍然不是我们想要的。引号仍未正确转义。哎呀。 在这里您可以开始输入 100 种不同的字符组合,以找出正确的组合是什么 ;-)。

或者,让我们回到 bash 并弄清楚可能发生了什么。首先,让我们记住正在 echoed 的字符串被解释为字符串。所以让我们在我们的 echo 语句周围加上双引号,这样它就被当作一个字符串来对待,看看它做了什么。请记住,我们想要的是输出与前面相同的 bash 命令,但带有反斜杠:

HAL9256@HAL9000:~$ echo "\'Hello\' \"World\"!"
\'Hello\' "World"!

好吧,最终我们要发送给 bash 的是带有反斜杠的原始字符串。在这里我们可以看到反斜杠正在转义双引号并消失。因此,添加更多斜线:

HAL9256@HAL9000:~$ echo "\'Hello\' \"World\"!"
\'Hello\' \World\!

哦,太好了。我们逃脱了反斜杠。像 Inception 一样,不断添加转义字符直到它起作用....

HAL9256@HAL9000:~$ echo "\'Hello\' \\"World\\"!"
\'Hello\' \"World\"!

到了!我们有我们的输出。现在我们必须将它输入 PowerShell:

PS C:\> bash -c "echo \'Hello\' \\"World\\"!"
/bin/bash: -c: line 0: unexpected EOF while looking for matching `"'
/bin/bash: -c: line 1: syntax error: unexpected end of file

哦!是的,和以前一样的错误。我们必须记住用反引号转义 PowerShell 中的双引号:

PS C:\> bash -c "echo \'Hello\' \\`"World\\`"!"
'Hello' "World"!

成功! bash-wize 和 PowerShell-wize 只需要大量转义。另一种方法,在我看来,更容易理解的在双引号内转义的方法是使用两个双引号而不是像这样的反引号:

PS C:\> bash -c "echo \'Hello\' \\""World\\""!"
'Hello' "World"!

,但让我试着用不同的方式来描述这个问题:

要获得所需的逐字输出,这就是您的命令需要的样子如果您运行它来自bash本身:

# From Bash itself
$ bash -c "echo \"'hello' \\"world\\"!\""
'hello' "world"!

Bash对上面字符串字面量的初始解析,其中\作为转义字符,意味着新的bash实例看到下面的逐字文本作为其-c参数:
echo "'hello' \"world\"!"
因此,如果您在 Bash 中直接 作为命令执行它,它也会产生所需的输出。

请注意 echo 参数 作为一个整体 是如何包含在(最初 \-escaped)"..." 中的,为了稳健:没有例如,如果 worldworld & space,则该命令将 break;类似地,world class 会变成 world class(由于 shell 扩展 而导致的空白归一化)。

由于涉及 2 层转义,因此需要许多 \ 个字符:

  • 转义为调用shell(本例中也是bash),以确保输入字符串在句法上有效: \" 嵌入单个 "\ 嵌入单个 \.

  • 转义为目标shell(bash),因此结果 字符串对于 it 在句法上是有效的,因为在这种情况下,结果字符串被 解析为命令

T运行设置命令以便从 PowerShell调用它 需要先调整层,这意味着使用 `,PowerShell 的转义字符,而不是 \.

一个直接的 t运行因此是:

# SHOULD work, but DOESN'T as of PowerShell 7.0
PS> bash -c "echo `"'hello' \`"world\`"!`""
hello          # !! WRONG output.

然而,如果您在 PowerShell 中直接 打印字符串文字 "echo `"'hello' \`"world\`"!`"" ,您会发现它正确地生成了 verbatim bash需要看到的字符串,如上图
(echo "'hello' \"world\"!")

它不起作用的原因是 PowerShell 向 外部程序 传递参数一直以来都与 嵌入式双精度函数有关引用:

,但简而言之,从 PowerShell 7.0 开始:

除了满足 PowerShell 的 自己的 语法要求 - 如果您调用 PowerShell 命令就足够了 - 您必须另外 \-转义传递给外部程序的参数中嵌入的"字符.

因此:

# OK, but the manual \-escaping shouldn't be necessary.
PS> bash -c "echo \`"'hello' \\`"world\\`"!\`""
'hello' "world"!

为什么当前行为被破坏:

shell 的工作是将 shell 自己的解析 verbatim 产生的参数传递给目标程序,做任何事情有必要在幕后确保 - 除了 PowerShell 自己的之外,您不必担心任何转义要求 =142=]:

  • 在 Windows,出于不幸的需要,这意味着 在幕后构建一个命令行应用双重-根据需要引用和转义;双引号 由 PowerShell 按需应用,它是 \-转义 " 个字符。 嵌入参数 中缺失。

  • 在类 Unix 平台上,不需要额外的工作:在那里,程序直接接收参数作为 array of verbatim 值。

虽然这个问题中讨论的特定命令恰好使 运行 转换为 PowerShell 相当简单,还有更多阴险的情况,其中额外需要 \-escape - 总是麻烦 - 也是意想不到的,因为这些命令在bash中按原样工作]本身:

PS> bash -c 'echo "hi, there"'
hi,   # !! " were stripped, so bash only saw `echo hi,` as the command string,
      # !! and `there` as a separate argument.
PS> /bin/echo '{ "foo": "bar" }' # Try to pass a JSON string
{ foo: bar } # !! BROKEN JSON