Bash 中的转义字符

评论 0 浏览 0 2021-11-02

1.绪论

源代码、命令行和大多数计算机交互在其最基本的层面上由字符组成。另一方面,大多数字符在普通键盘上无法用按键表示,许多字符根本无法打印,还有一组是复杂的控制字符。

在本教程中,我们将讨论Bash中的字符转义。首先,我们简要地描述机器如何表示字符。之后,我们探讨Bash中字符串的类型。接下来,我们将详细讨论纯Bash中的字符转义。最后,我们看一下涉及到转义的具体案例。

我们在Debian 11 (Bullseye)上用GNU Bash 5.1.4测试了本教程中的代码。它符合POSIX标准,应该可以在任何这样的环境中工作。

2.字符

通常,除了指针之外,用户还有一种方法来输入数据–文本。为了表示文本,机器使用一连串的字节。它们根据预定的代码表对字符进行编码,通常是ASCIIUnicode

字符的类型大致包括:

  • 可打印的字符(如:<a>, <exclamation-mark>, <one>, <slash>)和<NUL>;
  • 不可打印的控制字符(例如:<BEL>, <ESC>)。

由于我们要处理大量的字符,在本文中,我们使用角括号符号,用这些表格中的名称来表示它们,

重要的是,我们必须有办法写出我们需要的任何字符,无论是ASCII、Unicode,还是自定义编码。不幸的是,我们只有一组最小的键盘键来表示许多不同的文本符号。

3.Bash字符串

写和存储字符是两个独立的动作,因为没有一个键盘有每一个可能的符号的按键。

在Bash中,文本被存储为字符串。事实上,所有的Bash变量都只是字符串。它们通常是直接、单引号或双引号的序列。

重要的是,这些方法的区别在于,我们在一种上下文中解释或插入某些字符组合,并在另一种上下文中按字面意思理解它们。

3.1. 单引号

在单引号内,我们不插入任何内容:

$ text='a $(echo b) c'
$ echo "${text}"
a $(echo b) c

注意单引号内的所有文本是如何被保留的。没有进行插值,但这意味着我们也不能在任何情况下,在单引号内直接使用单引号。

3.2.双引号

使用双引号时,我们保留大多数字符的字面值:

$ text="a"
$ text="${text} $(echo "b") c"
$ echo "${text}"
a b c

首先,我们将<a>直接分配给text,只用[a]键。之后,我们通过${text}的变量扩展来获取其值。这里,$ <美元符号>的组合得到解释。最后,我们将这个值与一个表达式和另一个字符连接起来,再分配给text

3.3.没有引号

只要该字符串符合某些规则,我们就可以跳过引号:

$ text=a$(echo b)c
$ echo ${text}
abc

我们在下一节讨论其中的一些规则。

3.4.特别引证

以<美元符号>为前缀的双引号文本会导致一个字符串根据当前的语言环境进行翻译。因此,该字符串的最终翻译是双引号。重要的是,本文不处理区域性问题,并假定C是所有例子的默认语言

具有相同前缀的单引号文本的处理方式不同。 在这种情况下,转义字符将被替换。

在下一节中,我们将阐明转义的含义。

4.Bash字符转义

除单引号内的字符外,Bash 中具有特殊含义的字符必须进行转义以保留其字面值。在实践中,这主要是通过转义字符 <反斜杠>来完成的。在某些情况下,我们可能不得不采用其他方法。

让我们看看什么时候以及如何使用哪种方法。

4.1.双重引号

我们通过在一个字符前加上<反斜杠>来转义双引号字符串中的文本:

$ text1="a $(echo b) c"
$ text2="a \$(echo b) c"
$ echo "${text1}"
a b c
$ echo "${text2}"
a $(echo b) c

请注意,在text2的情况下,<美元符号>被转义了,失去了它的特殊功能,保留了它的字面意思。

这些都是特殊字符,可能需要转义以保留其在双引号中的字面意思

  • $ <美元符号>,例如:$()${}
  • `<重音符>,也被称为反引号运算符
  • ”<引号>,当我们需要在双引号中加入双引号时,就可以使用。
  • newline<换行>,在Linux下等同于<LF>。
  • \<反斜杠>,当在此列表中的字符前缀时,除了<感叹号>之外;
  • !<感叹号>,当历史扩展POSIX模式之外被启用时,通常情况是
  • ~ <波浪号>,当开始一个字符串时,以避免波浪号扩展和与$HOME目录的混淆

此外,当上述所有字符中除了一个(<感叹号>)之前的<反斜杠>前缀不被存储在字符串中

$ text="!event"
bash: !event: event not found
$ text="\a \$ \` \!event \\"
$ echo ${text}
\a $ ` \!event \

重要的是,<感叹号>是一个特殊的字符,其特殊含义可以通过以下方式忽略:

  • 以反斜杠为前缀(保持不变,与普通字符如<a>一样)。
  • 在字符串的末尾或空白字符之前使用它。
  • 用单引号把它括起来,以转义一个<感叹号>
  • 通过set +o histexpand来禁用历史扩展。
  • 处于POSIX模式下

最后,双引号字符串中的<反斜杠><换行>组合被忽略和删除。这只是意味着我们可以将一个字符串分散到几行,而不需要在其中添加换行符:

$ text="a \
> b"
$ echo "${text}"
a b

现在让我们来探讨一下Bash是如何处理没有任何引号的序列的。

4.2.没有引号

正如我们已经表明的那样,我们可以完全放弃引号,但这是有代价的

也就是说,任何不带引号的序列,如果不转义所有非字母数字或以下字符组中的字符,就无法统一:<逗号>、<句号>、<下划线>、<加号>、<冒号>、<商业-at>、<百分号>、<斜线>、<连字号>:

$ text=a\ \&\ b\ \&\ c
$ echo "${text}"
a & b & c

不使用引号的情况很少,甚至没有。

4.3.ANSI-C组合

当使用$’STRING_TEXT’时,单引号内的序列会扩展为一个字符串,并根据ANSI-C引号替换转义的字符:

$ echo $'\u0061'
a

\u转义序列将它后面的四位数字解释为Unicode ISO/IEC 10646表中的十六进制代码。

重要的是,在它们被识别的地方,我们可以使用uUx和类似的序列来放置任何字符,而无需进一步转义。注意,在这种情况下,转义将字符的特殊含义打开,而不是关闭。这是两种避免键盘上按键不足的方法。

此外,许多其他工具都使用ANSI-C标准

5.特殊案例

Bash是一个有内置命令和功能的shell。许多命令使用ANSI标准,但有些功能也在字符串中使用他们自己的特殊控制字符

请记住,我们通过Bash传递的任何字符串,首先会被Bash解释。这意味着上一节的所有规则都适用,但在这一节中我们可以在这些规则的基础上进行扩展。

5.1.Bash提示语

在使用Bash时,我们首先看到的是提示符。它通常显示一些关于机器、用户、当前目录等的有用信息。所有这些都作为默认值保存在变量P0P1P2P4中。

然而,我们可以修改这些变量。此外,我们可以使用终端控制字符来定制我们的提示:

$ echo "Current prompt: ${PS1}"
Current prompt: $
$ PS1='\t> '
00:00:10> echo "Current prompt: ${PS1}"
Current prompt: \t>

这些序列以<反斜杠>开始,就像我们已经看过的大多数序列一样。此外,还有[]转义序列,它们又增加了一层编码。

5.2.ANSI转义序列

在许多终端中,我们还可以使用其他转义码,如标准的ANSI转义序列。例如,有一些方法可以改变终端文本的颜色、光标位置、字体和其他选项。这些序列以<ESC>开头,因此得名:

$ PS1="TESTING\033[1K> "
> echo "Current prompt: ${PS1}"
Current prompt: TESTING\033[1K>

在这个例子中,所谓的控制序列引入器<ESC><左方括号>以参数 1 开始执行K命令。 因此,它清除了当前行开始的所有字符。正因为如此,TESTING这个词就不会出现在提示符中。

如前所述,ANSI和ANSI-C转义序列在整个Linux生态系统中被使用。例如,echoprintf都认识它们。我们需要-e参数来echo,但printf默认情况下是使用ANSI的。

5.3. printf

标准的内置printf(打印功能)命令也有它自己的特殊字符。

回想一下我们关于编写不带引号的字符串的讨论。在这种情况下,我们需要转义的字符就在下面脚本的输出中:

$ for code in {0..127}; do
>   printf -v chr '\\%o' "${code}"
>   printf -v chr "${chr}"
>   printf -v echr "%q" "${chr}"
>   if [[ "${chr}" != "${echr}" ]]; then
>     printf "%02X %-7s\n" "${code}" "${echr}"
>   fi
> done
00 ''
01 $'\001'
[...]
07 $'\a'
08 $'\b'
09 $'\t'
0A $'\n'
[...]

上面的片段浏览了ASCII表中的前128个字符。对于每个字符,它使用printf来提取并比较每个字符与它的转义形式。

首先,%o返回字符编码的八进制形式。接下来,这个值在printf中被重新使用,并加上<反斜杠>前缀,以得到结果字符。之后,在%q格式修改器的帮助下,我们得到一个转义版本的字符。最后,我们比较正常版本和转义版本,以确定我们是否需要转义这个字符,如果需要,我们就输出结果。

注意% <百分号>字符在printf参数中。我们可以用另一个<百分号>来转义,从而忽略其特殊含义:%%。这样就保留了字面的值。

5.4 参数的转换

从4.4版本开始,Bash支持参数转换。这个功能允许我们执行许多printf和其他内建程序的操作,但直接在Bash中进行。

例如,我们可以用echo ${[email protected]}来代替printf%q的作用:

$ text='\'
$ printf '%q\n' "${text}"
\\
$ echo "${[email protected]}"
'\'

正如我们已经学过的,@‘\’是等价的。

上述两种方法在多级转义时都非常有用:

$ text="6*6*6 equals 216"
$ text="$(printf '%q' "${text}")"
$ text="$(printf '%q' "${text}")"
$ echo "${text}"
6\\\*6\\\*6\\\ equals\\\ 216

事实上,如果没有办法自动执行这一操作,手动转义长行往往会导致许多错误。

5.5.命令行参数

许多标准的Bash内置程序使用 <hyphen> 参数名称前缀。此外,它们经常有@参数,在这之后,<连字符>的特殊解释就会停止。如果不使用,我们就没有办法转义这个字符来防止这种行为。

6.归纳总结

在本教程中,我们讨论了Bash中的字符转义。我们首先了解到字符有不同的编码表。此外,我们还看到有些字符是不可打印的,只是一个标记或命令文本。要想按字面意思使用这些字符,我们需要有办法将其转义。我们探索了纯Bash,以及一些常见的Bash内置字符转义情况。

总之,字符转义只是部分标准化,所以存在许多隐晦的场景和工具,其中转义字符是不容易的

最后更新2023-07-23
0 个评论
标签