shell 中的 "..."、'...'、$'...' 和 $"..." 引号之间有什么区别?

Mic*_*mer 57 shell bash zsh shell-script quoting

有时候,我看到shell脚本使用所有的引用一些文字,这些不同的方式:"..."'...'$'...',和$"..."。为什么有这么多不同类型的引用被使用?

他们的行为是否有所不同或影响了我在他们内部可以做的事情?

Mic*_*mer 78

所有这些都意味着不同的东西,你可以在它们里面写不同的东西(或者同样的东西,有不同的含义)。不同种类的引用在它们内部解释不同的转义序列 ( \something),或者允许或不允许$something在它们内部进行变量插值 ( ) 和其他类型的扩展。

简而言之:

  • '...' 完全是字面意思。
  • "..." 允许变量和嵌入的引号字符。
  • $'...'执行像 那样的字符转义\n,但不扩展变量。
  • $"..." 用于 Bash 和 ksh 中的人类语言翻译。

'单引号'

您在单引号之间写的任何内容都按字面处理,根本不进行处理。反斜杠和美元符号在那里没有特殊意义。这意味着您不能反斜杠转义字符(包括其他单引号!)、插入变量或使用任何其他 shell 功能。

所有这些例子的结果都是引号之间写的字面意思:

代码 结果
'hello world' 你好,世界
'/pkg/bin:$PATH' /pkg/bin:$PATH
'hello\nworld' 你好\n世界
'`echo abc`' `回声abc`
'I\'dn\'t've' 我没有

最后一个很复杂 - 有两个单引号字符串与一些未引用的文本一起运行。第一个包含I\. 未加引号的文本dn\'t包含在 shell 级别转义的单引号,因此它不会以带引号的字符串开头,而是作为文字字符 (so, dn't)包含在内。最后引用的字符串只是ve. 所有这些都以 shell 的通常工作方式组合成一个单词。

组合文字文本和变量的一个有点常见的习惯用法是像这样一起运行它们:

'let x="'$PATH\"
Run Code Online (Sandbox Code Playgroud)

会导致

let x="/usr/bin:/bin"
Run Code Online (Sandbox Code Playgroud)

作为单个单词(最好也用双引号引起$PATH来,以防万一 -变量中的空格或通配符可能会以其他方式处理- 但为了可读的运行示例,我没有)。


“双引号”

在双引号内,处理了两种扩展,可以使用反斜杠对字符进行转义,以防止处理扩展或转义。

双引号内有两类扩展:

在引号内,反斜杠可以通过将其放在$or之前来抑制这些扩展`。它还可以转义结束双引号,因此\"仅包含"在您的字符串或另一个反斜杠中。任何其他反斜杠都按字面保留 - 没有产生其他字符的转义,并且不会被删除。

其中一些示例的行为与以前不同,有些则没有:

代码 结果
"hello world" 你好,世界
"/pkg/bin:$PATH" /pkg/bin:/bin:/usr/bin
"hello\nworld" 你好\n世界
"hello\\nworld" 你好\n世界
"`echo abc`" 美国广播公司
"I\'dn\'t've" 我没有
"I'dn't've" 我没有
"I\"dn\"t've" 我没有

$'ANSI-C 引用'

这种引用允许处理 C 样式的反斜杠转义,但不允许处理嵌入的变量或替换。这是唯一一种支持字符转义的引用

这是 ksh 的扩展,现在 Bash、zsh 和其他一些 shell 也支持。这不是还没有POSIX标准,因此最大便携脚本不能使用它的一部分,但猛砸或ksh脚本是免费的。

所有这些转义符都可以与它们的 C 含义一起使用:\a, \b, \f, \n, \r, \t, \v, 以及字面转义符\\, \', \", 和\?。它们还支持扩展\e(转义字符)以及 Bash 和 ksh \cx由 Ctrl-x输入的内容,例如\cM回车)。Shell 有一系列自己的小扩展。

它还允许四种通用字符转义:

  • \nnn, 八进制值nnn的单个字节
  • \xHH, 单个字节的十六进制值HH
  • \uHHHH,十六进制索引为HHHH的 Unicode 代码点
  • \UHHHHHHHH, 十六进制索引为HHHHHHHH的 Unicode 代码点

所有这些数字在第一个之后都是可选的。

$并且`没有意义并且按字面保留,因此您不能在其中包含变量。

代码 结果
$'hello world' 你好,世界
$'/pkg/bin:$PATH' /pkg/bin:$PATH
$'hello\nworld' 你好
世界
$'`echo abc`' `回声abc`
$'I\'dn\'t\'ve' 我没有
$'\U1f574\u263A' ?

大部分这些转义您可以模拟使用printf命令,虽然POSIX只需要\\\a\b\f\n\r\t\v,和\nnn在那里工作。您可以使用命令替换嵌入printf如果需要的话里面的双引号:"Path:$(printf '\t')$PATH"


$"语言环境翻译"

这是一个特定于 ksh 和 Bash 的扩展,用于本地化自然语言文本字符串,并在消息目录中查找引号内的部分。它首先执行所有双引号扩展。如果在翻译数据库中找不到该字符串,则将其用作自己的翻译。内置假设是字符串是英文的。

您可能不想使用这个,但如果您看到它,您通常可以将其视为常规双引号。


需要注意的一点是,没有一种引用既允许嵌入参数扩展又允许嵌入字符转义。在您想要的大多数情况下,您最好(更安全)使用printf

printf 'New path: \e[1m%s\e[0m' "/pkg/bin:$PATH:"
Run Code Online (Sandbox Code Playgroud)

这清楚地区分了哪些部分需要进行字符转义,哪些部分是数据值。

另一个原因是所有这些引用样式都会在 shell 中创建一个“单词”,除非 在双引号内使用$@了数组扩展${x[@]}。两种单引号形式始终是一个单词,不再进一步扩展。