5.2 转义

转义是一种引用单字符的方法。通过在特殊字符前加上转义符 \ 来告诉shell按照字面意思去解释这个字符。

notice 需要注意的是,在一些特定的命令和工具,比如 echo 和 sed 中,转义字符通常会起到相反的效果,即可能会使得那些字符产生特殊含义。

在 echo 与 sed 命令中,转义字符的特殊含义

\n

换行(line feed)。

\r

回车(carriage return)。

\t

水平制表符。

\v

垂直制表符。

\b

退格。

\a

警报、响铃或闪烁。

\0xx

ASCII码的八进制形式,等价于 0nn,其中 nn 是数字。

important 在 $' ... ' 字符串扩展结构中可以通过转义八进制或十六进制的ASCII码形式给变量赋值,比如 quote=$'\042'。

样例 5-2. 转义字符

1. #!/bin/bash
2. # escaped.sh: 转义字符

4. ##############################################
5. ### 首先让我们先看一下转义字符的基本用法。 ###
6. ##############################################

8. # 转义新的一行。
9. # ------------

11. echo ""

13. echo "This will print
14. as two lines."
15. # This will print
16. # as two lines.

18. echo "This will print \
19. as one line."
20. # This will print as one line.

22. echo; echo

24. echo "============="

27. echo "\v\v\v\v"      # 按字面意思打印 \v\v\v\v
28. # 使用 echo 命令的 -e 选项来打印转义字符。
29. echo "============="
30. echo "VERTICAL TABS"
31. echo -e "\v\v\v\v"   # 打印四个垂直制表符。
32. echo "=============="

34. echo "QUOTATION MARK"
35. echo -e "\042"       # 打印 " (引号,八进制ASCII码为42)。
36. echo "=============="

40. # 使用 $'\X' 这样的形式后可以不需要加 -e 选项。

42. echo; echo "NEWLINE and (maybe) BEEP"
43. echo $'\n'           # 新的一行。
44. echo $'\a'           # 警报(响铃)。
45. # 根据不同的终端版本,也可能是闪屏。

47. # 我们之前介绍了 $'\nnn' 字符串扩展,而现在我们要看到的是...

49. # ============================================ #
50. # 自 Bash 第二个版本开始的 $'\nnn' 字符串扩展结构。
51. # ============================================ #

53. echo "Introducing the \$\' ... \' string-expansion construct . . . "
54. echo ". . . featuring more quotation marks."

56. echo $'\t \042 \t'   # 在制表符之间的引号。
57. # 需要注意的是 '\nnn' 是一个八进制的值。

59. # 字符串扩展同样适用于十六进制的值,格式是 $'\xhhh'。
60. echo $'\t \x22 \t'  # 在制表符之间的引号。
61. # 感谢 Greg Keraunen 指出这些。
62. # 在早期的 Bash 版本中允许使用 '\x022' 这样的形式。

64. echo

67. # 将 ASCII 码字符赋值给变量。
68. # -----------------------
69. quote=$'\042'        # 将 " 赋值给变量。
70. echo "$quote Quoted string $quote and this lies outside the quotes."

72. echo

74. # 连接多个 ASCII 码字符给变量。
75. triple_underline=$'\137\137\137'  # 137是 '_' ASCII码的八进制形式
76. echo "$triple_underline UNDERLINE $triple_underline"

78. echo

80. ABC=$'\101\102\103\010'           # 101,102,103是 A, B, C
81. # ASCII码的八进制形式。
82. echo $ABC

84. echo

86. escape=$'\033'                    # 033 是 ESC 的八进制形式
87. echo "\"escape\" echoes an $escape"
88. # 没有可见输出

90. echo

92. exit 0

下面是一个更加复杂的例子:

样例 5-3. 检测键盘输入

1. #!/bin/bash
2. # 作者:Sigurd Solaas,作于2011年4月20日
3. # 授权在《高级Bash脚本编程指南》中使用。
4. # 需要 Bash 版本高于4.2。

6. key="no value yet"
7. while true; do
8. clear
9. echo "Bash Extra Keys Demo. Keys to try:"
10. echo
11. echo "* Insert, Delete, Home, End, Page_Up and Page_Down"
12. echo "* The four arrow keys"
13. echo "* Tab, enter, escape, and space key"
14. echo "* The letter and number keys, etc."
15. echo
16. echo "    d = show date/time"
17. echo "    q = quit"
18. echo "================================"
19. echo

21. # 将独立的Home键值转换为数字7上的Home键值:
22. if [ "$key" = $'\x1b\x4f\x48' ]; then
23. key=$'\x1b\x5b\x31\x7e'
24. #   引用字符扩展结构。
25. fi

27. # 将独立的End键值转换为数字1上的End键值:
28. if [ "$key" = $'\x1b\x4f\x46' ]; then
29. key=$'\x1b\x5b\x34\x7e'
30. fi

32. case "$key" in
33. $'\x1b\x5b\x32\x7e')  # 插入
34. echo Insert Key
35. ;;
36. $'\x1b\x5b\x33\x7e')  # 删除
37. echo Delete Key
38. ;;
39. $'\x1b\x5b\x31\x7e')  # 数字7上的Home键
40. echo Home Key
41. ;;
42. $'\x1b\x5b\x34\x7e')  # 数字1上的End键
43. echo End Key
44. ;;
45. $'\x1b\x5b\x35\x7e')  # 上翻页
46. echo Page_Up
47. ;;
48. $'\x1b\x5b\x36\x7e')  # 下翻页
49. echo Page_Down
50. ;;
51. $'\x1b\x5b\x41')  # 上箭头
52. echo Up arrow
53. ;;
54. $'\x1b\x5b\x42')  # 下箭头
55. echo Down arrow
56. ;;
57. $'\x1b\x5b\x43')  # 右箭头
58. echo Right arrow
59. ;;
60. $'\x1b\x5b\x44')  # 左箭头
61. echo Left arrow
62. ;;
63. $'\x09')  # 制表符
64. echo Tab Key
65. ;;
66. $'\x0a')  # 回车
67. echo Enter Key
68. ;;
69. $'\x1b')  # ESC
70. echo Escape Key
71. ;;
72. $'\x20')  # 空格
73. echo Space Key
74. ;;
75. d)
76. date
77. ;;
78. q)
79. echo Time to quit...
80. echo
81. exit 0
82. ;;
83. *)
84. echo Your pressed: \'"$key"\'
85. ;;
86. esac

88. echo
89. echo "================================"

91. unset K1 K2 K3
92. read -s -N1 -p "Press a key: "
93. K1="$REPLY"
94. read -s -N2 -t 0.001
95. K2="$REPLY"
96. read -s -N1 -t 0.001
97. K3="$REPLY"
98. key="$K1$K2$K3"

100. done

102. exit $?

还可以查看样例 37-1。

\“

转义引号,指代自身。

1. echo "Hello"                     # Hello
2. echo "\"Hello\" ... he said."    # "Hello" ... he said.

\$

转义美元符号(跟在 \\$ 后的变量名将不会被引用)。

1. echo "\$variable01"           # $variable01
2. echo "The book cost \$7.98."  # The book cost $7.98.

\

转义反斜杠,指代自身。

1. echo "\\"  # 结果是 \

3. # 然而...

5. echo "\"   # 在命令行中会出现第二行并提示输入。
6. # 在脚本中会出错。

8. # 但是...

10. echo '\'   # 结果是 \

extra 根据转义符所在的上下文(强引用、弱引用,命令替换或者在 here document)的不同,它的行为也会有所不同。

``` 1. # 简单转义与引用 2. echo \z # z 3. echo \z # \z 4. echo '\z' # \z 5. ehco '\z' # \z 6. echo "\z" # \z 7. echo "\z" # \z

  1. 命令替换

  2. echo echo \z # z
  3. echo echo \\z # z
  4. echo echo \\\z # \z
  5. echo echo \\\\z # \z
  6. echo echo \\\\\\z # \z
  7. echo echo \\\\\\\z # \z
  8. echo echo "\z" # \z
  9. echo echo "\\z" # \z

  10. Here Document

  11. cat <<EOF
  12. \z
  13. EOF # \z

  14. cat <<EOF

  15. \z
  16. EOF # \z

  17. 以上例子由 Stéphane Chazelas 提供。

    ```

含有转义字符的字符串可以赋值给变量,但是仅仅将单一的转义符赋值给变量是不可行的。

``` 1. variable=\ 2. echo "$variable" 3. # 这样做会报如下错误: 4. # tesh.sh: : command not found 5. # 单独的转义符不能够赋值给变量。 6. # 7. # 事实上,"\" 转义了换行,实际效果是: 8. #+ variable=echo "$variable" 9. #+ 这是一个非法的赋值方式。

  1. variable=\
  2. 23skidoo
  3. echo "$variable" # 23skidoo
  4. 因为第二行是一个合法的赋值,因此不会报错。

  5. variable=\

  6. \^ 转义符后有一个空格

  7. echo "$variable" # 空格

  8. variable=\

  9. echo "$variable" # \

  10. variable=\\

  11. echo "$variable"
  12. 这样做会报如下错误:

  13. tesh.sh: \: command not found

  14. 第一个转义符转转义了第二个,但是第三个转义符仍旧转义的是换行,

  15. + 跟开始的那个例子一样,因此会报错。

  16. variable=\\

  17. echo "$variable" # \
  18. 第二个和第四个转义符被转义了,因此可行。

    ```

转义空格能够避免在命令参数列表中的字符分割问题。

1. file_list="/bin/cat /bin/gzip /bin/more /usr/bin/less /usr/bin/emacs-20.7"
2. # 将一系列文件作为命令的参数。

4. # 增加两个文件到列表中,并且列出整个表。
5. ls -l /usr/X11R6/bin/xsetroot /sbin/dump $file_list

7. echo "-------------------------------------------------------------------------"

9. # 如果我们转义了这些空格会怎样?
10. ls -l /usr/X11R6/bin/xsetroot\ /sbin/dump\ $file_list
11. # 错误:因为转义了两个空格,因此前三个文件被连接成了一个参数传递给了 'ls -l'

转义符也提供一种可以撰写多行命令的方式。通常,每一行是一个命令,但是转义换行后命令就可以在下一行继续撰写。

1. (cd /source/directory && tar cf - . ) | \
2. (cd /dest/directory && tar xpvf -)
3. # 回顾 Alan Cox 的目录树拷贝命令,但是把它拆成了两行。

5. # 或者你也可以:
6. tar cf - -C /source/directory . |
7. tar xpvf - -C /dest/directory
8. # 可以看下方的注释。
9. # (感谢 Stéphane Chazelas。)

extra 在脚本中,如果以 “|” 管道作为一行的结束字符,那么不需要加转义符 \ 也可以写多行命令。但是一个好的编程习惯就是在写多行命令的事后,无论什么情况都要在行尾加上转义符 \。

1. echo "foo
2. bar"
3. #foo
4. #bar

6. echo

8. echo 'foo
9. bar'    # 没有区别。
10. #foo
11. #bar

13. echo

15. echo foo\
16. bar     # 转义换行。
17. #foobar

19. echo

21. echo "foo\
22. bar"     # 没有区别,在弱引用中,\ 转义符仍旧转义了换行。
23. #foobar

25. echo

27. echo 'foo\
28. bar'     # 在强引用中,\ 就按照字面意思来解释了。
29. #foo\
30. #bar

32. # 由 Stéphane Chazelas 提供的例子。