第五章 引用(引号)
引号的特殊效果就是保护字符串中的特殊字符不被shell或shell脚本重新解释或者扩展。
(这里所说的"特殊"指的是一些字符在shell中具有特殊的意义,比如:*)
[root@localhost aaa]# ls
1.sh 2.txt file.txt File.txt
[root@localhost aaa]# ls [Ff]*
file.txt File.txt
[root@localhost aaa]# ls '[Ff]*'
ls: 无法访问[Ff]*: 没有那个文件或目录
特定的程序和工具能够重新解释或者扩展特殊的字符。引号的一个重要的作用就是保护命令行的参数,但还是允许正在调用的程序来扩展它
grep '[Ff]irst' *.txt
file1.txt:This is the first of file1.txt
file2.txt:This is the First of file2.txt
#grep [Ff]irst *.txt 在Bash下的行为也是这样(正则)
引号还可以抑制echo命令的换行作用
[root@localhost aaa]# echo $(ls -l)
总用量 4 -rwxr-xr-x 1 root root 64 5月 28 01:57 1.sh -rw-r--r-- 1 root root 0 6月 22 18:38 2.txt
[root@localhost aaa]# echo "$(ls -l)"
总用量 4
-rwxr-xr-x 1 root root 64 5月 28 01:57 1.sh
-rw-r--r-- 1 root root 0 6月 22 18:38 2.txt
5.1 引用变量
在一个双引号中直接使用变量名一般都是没有问题的。它阻止了所有在引号中的特殊字符的重新解释(包括变量名),但是$、`和\除外。
$ 作为特殊字符的意义,是为了能够在双引号中也能正常地引用变量。这样在""中可以使用变量表达式的值(Example 4.1)
使用""来防止单词分割。如果在参数列表中使用双引号,将使得双引号中的参数作为一个参数。即使双引号中的字符串包含多个单词,也不会边为多个参数。如:
variable1="a variable containing five words"
COMMAND This is $variable1 #COMMAND将以7个参数来执行
#"This" "is" "a" "variable" "containing" "five" "words"
COMMAND "This is $variable1" #COMMAND将以1个参数来执行
#"This is a variable containing five words"
variable2=""
COMMAND $variable2 $variable2 $variable2 #COMMADN将不带参数执行
COMMAND "$variable2" "$variable2" "$variable2" #COMMADN将以3个空参数去执行
COMMAND "$variable2 $variable2 $variable2" #COMMADN将以1个参数(2个空格)执行
用双引号把参数封到echo中是很有必要的,只有在单词分隔时或保留空白时可能有些问题。
Example 5.1 echo 一些诡异的变量
#!/bin/bash
#weiravars.sh:echo 诡异的变量
var="'(]\\{}\$\""
echo $var #'(]\{}$"
echo "$var" #'(]\{}$"
IFS='\'
echo $var #'(] {}$" \转换成了空格
echo "$var" #'(]\{}$"
exit 0
IFS
是一种set变量,当shel处理"命令替换"和"参数替换"时,shell根据IFS的值,默认是space, tab, newline来拆解读入的变量,然后对特殊字符进行处理,最后重新组合赋值给该变量。
[root@localhost ~]# echo "$IFS" #直接输出IFS看不到值
[root@localhost ~]# echo "$IFS" | od -b #转化为二进制就可以看到了
0000000 040 011 012 012
0000004
#"040"是空格,"011"是Tab,"012"是换行符"\n"
#最后一个 012 是因为 echo 默认是会换行的
实际应用中
#!/bin/bash
OLD_IFS=$IFS #保存原始值
IFS="" #改变IFS的值
...
...
IFS=$OLD_IFS #还原IFS的原始值
5.2 转义 ()
转义是一种引用单个字符的方法。一个具有特殊含义的字符前边放上一个转义字符()就告诉shell这个字符失去了特殊的含义。
值得注意的是,在某些特定的命令和工具中,比如echo和sed,转义字符往往会起到相反的效果,它反倒有可能引发出这个字符特殊的含义。
在echo和sed中所使用的含义
\n #意味着新的一行
\r #回车
\t #tab键
\v #vertical tab(垂直tab),查看前边的Ctl-K
\b #backspace,查看前边的Ctl-H
\a #alert(如beep或flash)
\0xx #转换成8进制ASCII解码,等价于oxx
Example 5.2 转义字符
#!/bin/bash
#escaped.sh:转义字符
echo "\v\v\v\v" #\v\v\v\v
echo -e "\v\v\v\v" #echo -e是输出转义字符,所以会输出4个垂直tab
echo "====================="
echo -e "\042" #"("的8进制ASCII码是42)
echo "====================="
#如果使用''($'\x')结构,就不需要-e了
echo $'\n' #新行
echo $'\a' #alert
echo "====================="
#8进制,结构是$'\nnn'
#[root@zhhs ~]# echo $'\t \042 \t'
# "
#16进制,结构是$'\xhhh'
#[root@zhhs ~]# echo $'\t \x22 \t'
# "
#分配ASCII字符到变量中
quote=$'\042'
echo "$quote This is a quoted string,$quote and this lies outside the quotes."
#" This is a quoted string," and this lies outside the quotes.
#变量中连续的ASCII char。
triple_underline=$'\137\137\137' #137是'_'的八进制ASCII码。
echo "$triple_underline UNDERLINE $triple_underline"
#___ UNDERLINE ___
exit 0
另一个关于$"字符串扩展的例子见Example 34.1
\" 表达引号本身
echo "Hello" #Hello
echo "\"Hello\",he said" #"Hello",he said
\$ $号本身,跟在\$后的变量名,将不能扩展
echo "\$variable" #$variable
\\ \号本身
echo "\\" #\
#相反的
echo "\"
#这会出现第二个命令提示符,说白了就是提示你命令不全,还需要一个",如果是在脚本里,就会给出一个错误
分配给变量的字符串的元素也会被转义,但是只把一个转义字符分配给变量将会报错。
variable=\
123qwe
echo "$variable" #123qwe。这里"\"被认为是一个续行符
variable=\ #转义一个空格
echo "$variable" #显示空格
#variable=\\\和variable=\是一样的,都是非法定义;但是variable=\\\\是可以的
variable=\\\\
echo "$variable" #\\
#转义一个空格,在命令行参数列表中将会阻止单词分割问题
ls -l /usr/local\ /bin/xsetroot\ /sbin/dump
#这里转义空格,会将/usr/local /bin/xsetroot /sbin/dump作为一个参数传递给ls -l
转义字符的续行功能
(cd /source/directory && tar -cf - .) | \
(cd /dest/directory && tar xpvf -)
#也可以使用如下方式:
tar cf - -C /source/directory . |
tar xpvf - -C /dest/directory
注意:如果一个脚本以|(管道)结束,那么一个(转义字符)就不用了非加不可了;但是一个好的shell脚本编写风格还是应该在行尾加上\,以增加可读性。
echo "foo
bar"
#foo
#bar
echo 'foo
bar'
#foo
#bar
echo foo\
bar
#foobar
echo "foo\
bar"
#foobar
echo 'foo\
bar'
#foo\
#bar