5.2 转义

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

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

echosed 命令中,转义字符的特殊含义

\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: 转义字符
  3. ##############################################
  4. ### 首先让我们先看一下转义字符的基本用法。 ###
  5. ##############################################
  6. # 转义新的一行。
  7. # ------------
  8. echo ""
  9. echo "This will print
  10. as two lines."
  11. # This will print
  12. # as two lines.
  13. echo "This will print \
  14. as one line."
  15. # This will print as one line.
  16. echo; echo
  17. echo "============="
  18. echo "\v\v\v\v" # 按字面意思打印 \v\v\v\v
  19. # 使用 echo 命令的 -e 选项来打印转义字符。
  20. echo "============="
  21. echo "VERTICAL TABS"
  22. echo -e "\v\v\v\v" # 打印四个垂直制表符。
  23. echo "=============="
  24. echo "QUOTATION MARK"
  25. echo -e "\042" # 打印 " (引号,八进制ASCII码为42)。
  26. echo "=============="
  27. # 使用 $'\X' 这样的形式后可以不需要加 -e 选项。
  28. echo; echo "NEWLINE and (maybe) BEEP"
  29. echo $'\n' # 新的一行。
  30. echo $'\a' # 警报(响铃)。
  31. # 根据不同的终端版本,也可能是闪屏。
  32. # 我们之前介绍了 $'\nnn' 字符串扩展,而现在我们要看到的是...
  33. # ============================================ #
  34. # 自 Bash 第二个版本开始的 $'\nnn' 字符串扩展结构。
  35. # ============================================ #
  36. echo "Introducing the \$\' ... \' string-expansion construct . . . "
  37. echo ". . . featuring more quotation marks."
  38. echo $'\t \042 \t' # 在制表符之间的引号。
  39. # 需要注意的是 '\nnn' 是一个八进制的值。
  40. # 字符串扩展同样适用于十六进制的值,格式是 $'\xhhh'。
  41. echo $'\t \x22 \t' # 在制表符之间的引号。
  42. # 感谢 Greg Keraunen 指出这些。
  43. # 在早期的 Bash 版本中允许使用 '\x022' 这样的形式。
  44. echo
  45. # 将 ASCII 码字符赋值给变量。
  46. # -----------------------
  47. quote=$'\042' # 将 " 赋值给变量。
  48. echo "$quote Quoted string $quote and this lies outside the quotes."
  49. echo
  50. # 连接多个 ASCII 码字符给变量。
  51. triple_underline=$'\137\137\137' # 137是 '_' ASCII码的八进制形式
  52. echo "$triple_underline UNDERLINE $triple_underline"
  53. echo
  54. ABC=$'\101\102\103\010' # 101,102,103是 A, B, C
  55. # ASCII码的八进制形式。
  56. echo $ABC
  57. echo
  58. escape=$'\033' # 033 是 ESC 的八进制形式
  59. echo "\"escape\" echoes an $escape"
  60. # 没有可见输出
  61. echo
  62. exit 0

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

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

  1. #!/bin/bash
  2. # 作者:Sigurd Solaas,作于2011年4月20日
  3. # 授权在《高级Bash脚本编程指南》中使用。
  4. # 需要 Bash 版本高于4.2。
  5. key="no value yet"
  6. while true; do
  7. clear
  8. echo "Bash Extra Keys Demo. Keys to try:"
  9. echo
  10. echo "* Insert, Delete, Home, End, Page_Up and Page_Down"
  11. echo "* The four arrow keys"
  12. echo "* Tab, enter, escape, and space key"
  13. echo "* The letter and number keys, etc."
  14. echo
  15. echo " d = show date/time"
  16. echo " q = quit"
  17. echo "================================"
  18. echo
  19. # 将独立的Home键值转换为数字7上的Home键值:
  20. if [ "$key" = $'\x1b\x4f\x48' ]; then
  21. key=$'\x1b\x5b\x31\x7e'
  22. # 引用字符扩展结构。
  23. fi
  24. # 将独立的End键值转换为数字1上的End键值:
  25. if [ "$key" = $'\x1b\x4f\x46' ]; then
  26. key=$'\x1b\x5b\x34\x7e'
  27. fi
  28. case "$key" in
  29. $'\x1b\x5b\x32\x7e') # 插入
  30. echo Insert Key
  31. ;;
  32. $'\x1b\x5b\x33\x7e') # 删除
  33. echo Delete Key
  34. ;;
  35. $'\x1b\x5b\x31\x7e') # 数字7上的Home键
  36. echo Home Key
  37. ;;
  38. $'\x1b\x5b\x34\x7e') # 数字1上的End键
  39. echo End Key
  40. ;;
  41. $'\x1b\x5b\x35\x7e') # 上翻页
  42. echo Page_Up
  43. ;;
  44. $'\x1b\x5b\x36\x7e') # 下翻页
  45. echo Page_Down
  46. ;;
  47. $'\x1b\x5b\x41') # 上箭头
  48. echo Up arrow
  49. ;;
  50. $'\x1b\x5b\x42') # 下箭头
  51. echo Down arrow
  52. ;;
  53. $'\x1b\x5b\x43') # 右箭头
  54. echo Right arrow
  55. ;;
  56. $'\x1b\x5b\x44') # 左箭头
  57. echo Left arrow
  58. ;;
  59. $'\x09') # 制表符
  60. echo Tab Key
  61. ;;
  62. $'\x0a') # 回车
  63. echo Enter Key
  64. ;;
  65. $'\x1b') # ESC
  66. echo Escape Key
  67. ;;
  68. $'\x20') # 空格
  69. echo Space Key
  70. ;;
  71. d)
  72. date
  73. ;;
  74. q)
  75. echo Time to quit...
  76. echo
  77. exit 0
  78. ;;
  79. *)
  80. echo Your pressed: \'"$key"\'
  81. ;;
  82. esac
  83. echo
  84. echo "================================"
  85. unset K1 K2 K3
  86. read -s -N1 -p "Press a key: "
  87. K1="$REPLY"
  88. read -s -N2 -t 0.001
  89. K2="$REPLY"
  90. read -s -N1 -t 0.001
  91. K3="$REPLY"
  92. key="$K1$K2$K3"
  93. done
  94. 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 "\\" # 结果是 \
  2. # 然而...
  3. echo "\" # 在命令行中会出现第二行并提示输入。
  4. # 在脚本中会出错。
  5. # 但是...
  6. 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
  8. # 命令替换
  9. echo `echo \z` # z
  10. echo `echo \\z` # z
  11. echo `echo \\\z` # \z
  12. echo `echo \\\\z` # \z
  13. echo `echo \\\\\\z` # \z
  14. echo `echo \\\\\\\z` # \\z
  15. echo `echo "\z"` # \z
  16. echo `echo "\\z"` # \z
  17. # Here Document
  18. cat <<EOF
  19. \z
  20. EOF # \z
  21. cat <<EOF
  22. \\z
  23. EOF # \z
  24. # 以上例子由 Stéphane Chazelas 提供。

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

  1. variable=\
  2. echo "$variable"
  3. # 这样做会报如下错误:
  4. # tesh.sh: : command not found
  5. # 单独的转义符不能够赋值给变量。
  6. #
  7. # 事实上,"\" 转义了换行,实际效果是:
  8. #+ variable=echo "$variable"
  9. #+ 这是一个非法的赋值方式。
  10. variable=\
  11. 23skidoo
  12. echo "$variable" # 23skidoo
  13. # 因为第二行是一个合法的赋值,因此不会报错。
  14. variable=\
  15. # \^ 转义符后有一个空格
  16. echo "$variable" # 空格
  17. variable=\\
  18. echo "$variable" # \
  19. variable=\\\
  20. echo "$variable"
  21. # 这样做会报如下错误:
  22. # tesh.sh: \: command not found
  23. #
  24. # 第一个转义符转转义了第二个,但是第三个转义符仍旧转义的是换行,
  25. #+ 跟开始的那个例子一样,因此会报错。
  26. variable=\\\\
  27. echo "$variable" # \\
  28. # 第二个和第四个转义符被转义了,因此可行。

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

  1. file_list="/bin/cat /bin/gzip /bin/more /usr/bin/less /usr/bin/emacs-20.7"
  2. # 将一系列文件作为命令的参数。
  3. # 增加两个文件到列表中,并且列出整个表。
  4. ls -l /usr/X11R6/bin/xsetroot /sbin/dump $file_list
  5. echo "-------------------------------------------------------------------------"
  6. # 如果我们转义了这些空格会怎样?
  7. ls -l /usr/X11R6/bin/xsetroot\ /sbin/dump\ $file_list
  8. # 错误:因为转义了两个空格,因此前三个文件被连接成了一个参数传递给了 'ls -l'

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

  1. (cd /source/directory && tar cf - . ) | \
  2. (cd /dest/directory && tar xpvf -)
  3. # 回顾 Alan Cox 的目录树拷贝命令,但是把它拆成了两行。
  4. # 或者你也可以:
  5. tar cf - -C /source/directory . |
  6. tar xpvf - -C /dest/directory
  7. # 可以看下方的注释。
  8. # (感谢 Stéphane Chazelas。)

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

  1. echo "foo
  2. bar"
  3. #foo
  4. #bar
  5. echo
  6. echo 'foo
  7. bar' # 没有区别。
  8. #foo
  9. #bar
  10. echo
  11. echo foo\
  12. bar # 转义换行。
  13. #foobar
  14. echo
  15. echo "foo\
  16. bar" # 没有区别,在弱引用中,\ 转义符仍旧转义了换行。
  17. #foobar
  18. echo
  19. echo 'foo\
  20. bar' # 在强引用中,\ 就按照字面意思来解释了。
  21. #foo\
  22. #bar
  23. # 由 Stéphane Chazelas 提供的例子。