# 5.2 转义
转义是一种引用单字符的方法。通过在特殊字符前加上转义符 `\` 来告诉shell按照字面意思去解释这个字符。
> ![notice](http://tldp.org/LDP/abs/images/caution.gif) 需要注意的是,在一些特定的命令和工具,比如 `echo` 和 `sed` 中,转义字符通常会起到相反的效果,即可能会使得那些字符产生特殊含义。
在 `echo` 与 `sed` 命令中,转义字符的特殊含义
### \n
换行(line feed)。
### \r
回车(carriage return)。
### \t
水平制表符。
### \v
垂直制表符。
### \b
退格。
### \a
警报、响铃或闪烁。
### \0xx
ASCII码的八进制形式,等价于 `0nn`,其中 `nn` 是数字。
> ![important](http://tldp.org/LDP/abs/images/important.gif) 在 `$' ... '` 字符串扩展结构中可以通过转义八进制或十六进制的ASCII码形式给变量赋值,比如 `quote=$'\042'`。
样例 5-2. 转义字符
```bash
#!/bin/bash
# escaped.sh: 转义字符
##############################################
### 首先让我们先看一下转义字符的基本用法。 ###
##############################################
# 转义新的一行。
# ------------
echo ""
echo "This will print
as two lines."
# This will print
# as two lines.
echo "This will print \
as one line."
# This will print as one line.
echo; echo
echo "============="
echo "\v\v\v\v" # 按字面意思打印 \v\v\v\v
# 使用 echo 命令的 -e 选项来打印转义字符。
echo "============="
echo "VERTICAL TABS"
echo -e "\v\v\v\v" # 打印四个垂直制表符。
echo "=============="
echo "QUOTATION MARK"
echo -e "\042" # 打印 " (引号,八进制ASCII码为42)。
echo "=============="
# 使用 $'\X' 这样的形式后可以不需要加 -e 选项。
echo; echo "NEWLINE and (maybe) BEEP"
echo $'\n' # 新的一行。
echo $'\a' # 警报(响铃)。
# 根据不同的终端版本,也可能是闪屏。
# 我们之前介绍了 $'\nnn' 字符串扩展,而现在我们要看到的是...
# ============================================ #
# 自 Bash 第二个版本开始的 $'\nnn' 字符串扩展结构。
# ============================================ #
echo "Introducing the \$\' ... \' string-expansion construct . . . "
echo ". . . featuring more quotation marks."
echo $'\t \042 \t' # 在制表符之间的引号。
# 需要注意的是 '\nnn' 是一个八进制的值。
# 字符串扩展同样适用于十六进制的值,格式是 $'\xhhh'。
echo $'\t \x22 \t' # 在制表符之间的引号。
# 感谢 Greg Keraunen 指出这些。
# 在早期的 Bash 版本中允许使用 '\x022' 这样的形式。
echo
# 将 ASCII 码字符赋值给变量。
# -----------------------
quote=$'\042' # 将 " 赋值给变量。
echo "$quote Quoted string $quote and this lies outside the quotes."
echo
# 连接多个 ASCII 码字符给变量。
triple_underline=$'\137\137\137' # 137是 '_' ASCII码的八进制形式
echo "$triple_underline UNDERLINE $triple_underline"
echo
ABC=$'\101\102\103\010' # 101,102,103是 A, B, C
# ASCII码的八进制形式。
echo $ABC
echo
escape=$'\033' # 033 是 ESC 的八进制形式
echo "\"escape\" echoes an $escape"
# 没有可见输出
echo
exit 0
```
下面是一个更加复杂的例子:
样例 5-3. 检测键盘输入
```bash
#!/bin/bash
# 作者:Sigurd Solaas,作于2011年4月20日
# 授权在《高级Bash脚本编程指南》中使用。
# 需要 Bash 版本高于4.2。
key="no value yet"
while true; do
clear
echo "Bash Extra Keys Demo. Keys to try:"
echo
echo "* Insert, Delete, Home, End, Page_Up and Page_Down"
echo "* The four arrow keys"
echo "* Tab, enter, escape, and space key"
echo "* The letter and number keys, etc."
echo
echo " d = show date/time"
echo " q = quit"
echo "================================"
echo
# 将独立的Home键值转换为数字7上的Home键值:
if [ "$key" = $'\x1b\x4f\x48' ]; then
key=$'\x1b\x5b\x31\x7e'
# 引用字符扩展结构。
fi
# 将独立的End键值转换为数字1上的End键值:
if [ "$key" = $'\x1b\x4f\x46' ]; then
key=$'\x1b\x5b\x34\x7e'
fi
case "$key" in
$'\x1b\x5b\x32\x7e') # 插入
echo Insert Key
;;
$'\x1b\x5b\x33\x7e') # 删除
echo Delete Key
;;
$'\x1b\x5b\x31\x7e') # 数字7上的Home键
echo Home Key
;;
$'\x1b\x5b\x34\x7e') # 数字1上的End键
echo End Key
;;
$'\x1b\x5b\x35\x7e') # 上翻页
echo Page_Up
;;
$'\x1b\x5b\x36\x7e') # 下翻页
echo Page_Down
;;
$'\x1b\x5b\x41') # 上箭头
echo Up arrow
;;
$'\x1b\x5b\x42') # 下箭头
echo Down arrow
;;
$'\x1b\x5b\x43') # 右箭头
echo Right arrow
;;
$'\x1b\x5b\x44') # 左箭头
echo Left arrow
;;
$'\x09') # 制表符
echo Tab Key
;;
$'\x0a') # 回车
echo Enter Key
;;
$'\x1b') # ESC
echo Escape Key
;;
$'\x20') # 空格
echo Space Key
;;
d)
date
;;
q)
echo Time to quit...
echo
exit 0
;;
*)
echo Your pressed: \'"$key"\'
;;
esac
echo
echo "================================"
unset K1 K2 K3
read -s -N1 -p "Press a key: "
K1="$REPLY"
read -s -N2 -t 0.001
K2="$REPLY"
read -s -N1 -t 0.001
K3="$REPLY"
key="$K1$K2$K3"
done
exit $?
```
还可以查看样例 37-1。
### \\"
转义引号,指代自身。
```bash
echo "Hello" # Hello
echo "\"Hello\" ... he said." # "Hello" ... he said.
```
### \\$
转义美元符号(跟在 `\\$` 后的变量名将不会被引用)。
```bash
echo "\$variable01" # $variable01
echo "The book cost \$7.98." # The book cost $7.98.
```
### \\\\
转义反斜杠,指代自身。
```bash
echo "\\" # 结果是 \
# 然而...
echo "\" # 在命令行中会出现第二行并提示输入。
# 在脚本中会出错。
# 但是...
echo '\' # 结果是 \
```
> ![extra](http://tldp.org/LDP/abs/images/note.gif) 根据转义符所在的上下文(强引用、弱引用,命令替换或者在 here document)的不同,它的行为也会有所不同。
>
```bash
# 简单转义与引用
echo \z # z
echo \\z # \z
echo '\z' # \z
ehco '\\z' # \\z
echo "\z" # \z
echo "\\z" # \z
>
# 命令替换
echo `echo \z` # z
echo `echo \\z` # z
echo `echo \\\z` # \z
echo `echo \\\\z` # \z
echo `echo \\\\\\z` # \z
echo `echo \\\\\\\z` # \\z
echo `echo "\z"` # \z
echo `echo "\\z"` # \z
>
# Here Document
cat <<EOF
\z
EOF # \z
>
cat <<EOF
\\z
EOF # \z
>
# 以上例子由 Stéphane Chazelas 提供。
```
> 含有转义字符的字符串可以赋值给变量,但是仅仅将单一的转义符赋值给变量是不可行的。
>
```bash
variable=\
echo "$variable"
# 这样做会报如下错误:
# tesh.sh: : command not found
# 单独的转义符不能够赋值给变量。
#
# 事实上,"\" 转义了换行,实际效果是:
#+ variable=echo "$variable"
#+ 这是一个非法的赋值方式。
>
variable=\
23skidoo
echo "$variable" # 23skidoo
# 因为第二行是一个合法的赋值,因此不会报错。
>
variable=\
# \^ 转义符后有一个空格
echo "$variable" # 空格
>
variable=\\
echo "$variable" # \
>
variable=\\\
echo "$variable"
# 这样做会报如下错误:
# tesh.sh: \: command not found
#
# 第一个转义符转转义了第二个,但是第三个转义符仍旧转义的是换行,
#+ 跟开始的那个例子一样,因此会报错。
>
variable=\\\\
echo "$variable" # \\
# 第二个和第四个转义符被转义了,因此可行。
```
转义空格能够避免在命令参数列表中的字符分割问题。
```bash
file_list="/bin/cat /bin/gzip /bin/more /usr/bin/less /usr/bin/emacs-20.7"
# 将一系列文件作为命令的参数。
# 增加两个文件到列表中,并且列出整个表。
ls -l /usr/X11R6/bin/xsetroot /sbin/dump $file_list
echo "-------------------------------------------------------------------------"
# 如果我们转义了这些空格会怎样?
ls -l /usr/X11R6/bin/xsetroot\ /sbin/dump\ $file_list
# 错误:因为转义了两个空格,因此前三个文件被连接成了一个参数传递给了 'ls -l'
```
转义符也提供一种可以撰写多行命令的方式。通常,每一行是一个命令,但是转义换行后命令就可以在下一行继续撰写。
```bash
(cd /source/directory && tar cf - . ) | \
(cd /dest/directory && tar xpvf -)
# 回顾 Alan Cox 的目录树拷贝命令,但是把它拆成了两行。
# 或者你也可以:
tar cf - -C /source/directory . |
tar xpvf - -C /dest/directory
# 可以看下方的注释。
# (感谢 Stéphane Chazelas。)
```
> ![extra](http://tldp.org/LDP/abs/images/note.gif) 在脚本中,如果以 "|" 管道作为一行的结束字符,那么不需要加转义符 \ 也可以写多行命令。但是一个好的编程习惯就是在写多行命令的事后,无论什么情况都要在行尾加上转义符 \。
```bash
echo "foo
bar"
#foo
#bar
echo
echo 'foo
bar' # 没有区别。
#foo
#bar
echo
echo foo\
bar # 转义换行。
#foobar
echo
echo "foo\
bar" # 没有区别,在弱引用中,\ 转义符仍旧转义了换行。
#foobar
echo
echo 'foo\
bar' # 在强引用中,\ 就按照字面意思来解释了。
#foo\
#bar
# 由 Stéphane Chazelas 提供的例子。
```
- 第一部分 初见shell
- 1. 为什么使用shell编程
- 2. 和Sha-Bang(#!)一起出发
- 2.1 调用一个脚本
- 2.2 牛刀小试
- 第二部分 shell基础
- 3. 特殊字符
- 4. 变量与参数
- 4.1 变量替换
- 4.2 变量赋值
- 4.3 Bash弱类型变量
- 4.4 特殊变量类型
- 5. 引用
- 5.1 引用变量
- 5.2 转义
- 6. 退出与退出状态
- 7. 测试
- 7.1 测试结构
- 7.2 文件测试操作
- 7.3 其他比较操作
- 7.4 嵌套 if/then 条件测试
- 7.5 牛刀小试
- 8. 运算符相关话题
- 8.1 运算符
- 8.2 数字常量
- 8.3 双圆括号结构
- 8.4 运算符优先级
- 第三部分 shell进阶
- 10. 变量处理
- 10.1 字符串处理
- 10.1.1 使用 awk 处理字符串
- 10.1.2 参考资料
- 10.2 参数替换
- 11. 循环与分支
- 11.1 循环
- 11.2 嵌套循环
- 11.3 循环控制
- 11.4 测试与分支
- 12. 命令替换
- 13. 算术扩展
- 14. 休息时间
- 第五部分 进阶话题
- 19. 嵌入文档
- 20. I/O 重定向
- 20.1 使用 exec
- 20.2 重定向代码块
- 20.3 应用程序
- 22. 限制模式的Shell
- 23. 进程替换
- 26. 列表结构
- 25. 别名