高级shell编程技巧5(转载)
时间:2006-06-02 来源:lin_lin13
第5章 引用(翻译的可能有问题,特指引号)
======================================
引号的特殊效果就是,保护字符串中的特殊字符不被shell或者是shell脚本重新解释或者扩展.
(我们这里所说的"特殊"指的是一些字符在shell中具有的特殊意义,比如*)
如:
bash$ ls -l [Vv]*
-rw-rw-r-- 1 bozo bozo 324 Apr 2 15:05 VIEWDATA.BAT
-rw-rw-r-- 1 bozo bozo 507 May 4 14:25 vartrace.sh
-rw-rw-r-- 1 bozo bozo 539 Apr 14 17:11 viewdata.sh
bash$ ls -l '[Vv]*'
ls: [Vv]*: No such file or directory
在我们一般的生活中,引号内的内容往往有特殊的含义,而在Bash中,当我们引用一个字符串,
我们是保护它的字面含义.
特定的程序和工具能够重新解释或扩展特殊的字符.引用的一个重要的作用就是保护命令行中
的参数,但还是允许正在调用的程序来扩展它.
bash$ grep '[Ff]irst' *.txt
file1.txt:This is the first line of file1.txt.
file2.txt:This is the First line of file2.txt.
注意 grep [Ff]irst *.txt在Bash下的行为(其实就是正则表达式么),[1]
引用还可以抑制echo命令的换行作用.
bash$ echo $(ls -l)
total 8 -rw-rw-r-- 1 bozo bozo 130 Aug 21 12:57 t222.sh -rw-rw-r-- 1 bozo bozo 78 Aug 21 12:57 t71.sh
bash$ echo "$(ls -l)"
total 8
-rw-rw-r-- 1 bozo bozo 130 Aug 21 12:57 t222.sh
-rw-rw-r-- 1 bozo bozo 78 Aug 21 12:57 t71.sh
5.1 引用变量
------------
在一个双引号中直接使用变量名,一般都是没有问题的.它阻止了所有在引号中的特殊字符的
重新解释--包括变量名[2]--但是$,`和\除外.[3]保留$,作为特殊字符的意义,是为了能够在双
引号中也能够正常地引用变量("$var").这样在""中可以使用变量所表达的值(Example 4-1).
使用""来防止单词分割.[4]如果在参数列表中使用双引号,将使得双引号中的参数作为一个参
数.即使双引号中的字符串包含多个单词(也就是包含空白部分),也不会变为多个参数,如:
1 variable1="a variable containing five words"
2 COMMAND This is $variable1 # COMMAND将以7个参数来执行
3 # "This" "is" "a" "variable" "containing" "five" "words"
4
5 COMMAND "This is $variable1" # COMMAND将以1个参数来执行
6 # "This is a variable containing five words"
7
8
9 variable2="" # 空值
10
11 COMMAND $variable2 $variable2 $variable2 # COMMAND将不带参数执行
12 COMMAND "$variable2" "$variable2" "$variable2" # COMMAND将以3个空参数来执行
13 COMMAND "$variable2 $variable2 $variable2" # COMMAND将以1个参数来执行(2空格)
用双引号把参数封到echo中是很有必要的,只有在单词分隔或时保留空白时的时候可能
有些问题.
Example 5-1 echo一些诡异的变量
################################Start Script#######################################
1 #!/bin/bash
2 # weirdvars.sh: echo诡异的变量
3
4 var="'(]\\{}\$\""
5 echo $var # '(]\{}$"
6 echo "$var" # '(]\{}$" 并没有什么不同
7
8 echo
9
10 IFS='\'
11 echo $var # '(] {}$" \ 转换成空格了?明显和IFS有关系么!又不傻!
12 echo "$var" # '(]\{}$"
13
14 exit 0
################################End Script#########################################
单引号操作总体上和""很像,但不允许引用变量.因为$的特殊含义被关闭了.在''中除了',其他
字符都没有特殊的含义了.所以单引号比双引号严格.
因为即使是\,在''中都被关闭了,所以你想在''中显示'的含义,将得不到预期的效果.
1 echo "Why can't I write 's between single quotes"
2
3 echo
4
5 # 一种绕弯的方法
6 echo 'Why can'\''t I write '"'"'s between single quotes'
7 # |-------| |----------| |-----------------------|
8 # 包含了2个单引号字符,原书好像有错误
注意事项:
[1] 除非当前目录下,正好有个叫first的文件.
[2] 即使是变量的值也是有副作用的(见下边)
[3] 如果在""中包含"!"的话,在命令行中将会出现错误.因为这个"!"被当作历史命令来解释了.
在一个脚本中,这种情况是不会发生的,因为在脚本中,Bash历史记录被关闭了.
下边是一些关于"\"一些不协调的行为.
bash$ echo hello\!
hello!
bash$ echo "hello\!"
hello\!
bash$ echo -e x\ty
xty
bash$ echo -e "x\ty"
x y
[4] "单词分隔",在这个上下文中意味着,将一个字符串分隔为一些分离的参数.
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 转义符
################################Start Script#######################################
1 #!/bin/bash
2 # escaped.sh: 转义符
3
4 echo; echo
5
6 echo "\v\v\v\v" # 逐字的打印\v\v\v\v .
7 # 使用-e选项的echo命令来打印转义符
8 echo "============="
9 echo "VERTICAL TABS"
10 echo -e "\v\v\v\v" # Prints 4 vertical tabs.
11 echo "=============="
12
13 echo "QUOTATION MARK"
14 echo -e "\042" # 打印" (引号, 8进制的ASCII 码就是42).
15 echo "=============="
16
17 # The $'\X' construct makes the -e option unnecessary.
17 # 如果使用$'\X'结构,那-e选项就不必要了
18 echo; echo "NEWLINE AND BEEP"
19 echo $'\n' # 新行.
20 echo $'\a' # Alert (beep).
21
22 echo "==============="
23 echo "QUOTATION MARKS"
24 # 版本2以后Bash允许使用$'\nnn'结构
25 # 注意这种情况,'\nnn\是8进制
26 echo $'\t \042 \t' # Quote (") framed by tabs.
27
28 # 当然,也可以使用16进制的值,使用$'\xhhh' 结构
29 echo $'\t \x22 \t' # Quote (") framed by tabs.
30
31 # 早一点的Bash版本允许'\x022'这种形式
32 echo "==============="
33 echo
34
35
36 # 分配ASCII字符到变量中
37 # ---------------------
38 quote=$'\042' # \042是",分配到变量中
39 echo "$quote This is a quoted string, $quote and this lies outside the quotes."
40
41 echo
42
43 # Concatenating ASCII chars in a variable.
43 # 变量中的连续的ASCII char.
44 triple_underline=$'\137\137\137' # 137 是8进制的ASCII 码'_'.
45 echo "$triple_underline UNDERLINE $triple_underline"
46
47 echo
48
49 ABC=$'\101\102\103\010' # 101, 102, 103 是8进制的码A, B, C.
50 echo $ABC
51
52 echo; echo
53
54 escape=$'\033' # 033 是8进制码for escape.
55 echo "\"escape\" echoes as $escape"
56 #"escape" echoes as 没有变量被输出
57
58 echo; echo
59
60 exit 0
################################End Script#########################################
另一个关于$''字符串扩展结果的例子见Example 34-1
\" 表达引号本身
1 echo "Hello" # Hello
2 echo "\"Hello\", he said." # "Hello", he said.
\$ $号本身,跟在\$后的变量名,将不能扩展
1 echo "\$variable01" # 结果是$variable01
\\ \号本身.
1 echo "\\" # 结果是\
2
3 # 相反的 . . .
4
5 echo "\" # 这会出现第2个命令提示符,说白了就是提示你命令不全,你再补个"就
6 # 好了.如果是在脚本里,就会给出一个错误.
注意:\的行为依赖于它是否被转义,被"",或者是否在"命令替换"和"here document"中.
################################Start Script#######################################
1 # 简单的转义和""
2 echo \z # z
3 echo \\z # \z
4 echo '\z' # \z
5 echo '\\z' # \\z
6 echo "\z" # \z
7 echo "\\z" # \z
8
9 # 命令替换
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 echo `echo "\\z"` # \z
18
19 # Here document
20 cat <<EOF
21 \z
22 EOF # \z
23
24 cat <<EOF
25 \\z
26 EOF # \z
################################End Script#########################################
分配给变量的字符串的元素也会被转义,但是只把一个转义符分配给变量将会报错.
################################Start Script#######################################
1 variable=\
2 echo "$variable"
3 # Will not work - gives an error message:
3 # 将不能正常工作- 将给出一个错误消息:
4 # test.sh: : command not found
5 # 一个"裸体的" 转义符将不能够安全的分配给变量.
6 #
7 # What actually happens here is that the "\" escapes the newline and
7 # 这里其实真正发生的是variable=\,这句被shell认为是没有完成,\被认为是一个续行符
8 #+ 这样,下边的这句echo,也被认为是上一行的补充.所以,总的来说就是一个非法变量分配
9
10 variable=\
11 23skidoo
12 echo "$variable" # 23skidoo
13 # 这句就可以使用,因为这是一个合法的变量分配
14
15 variable=\
16 # \^ 转义一个空格
17 echo "$variable" # 显示空格
18
19 variable=\\
20 echo "$variable" # \
21
22 variable=\\\
23 echo "$variable"
24 # 不能正常工作,给出一个错误
25 # test.sh: \: command not found
26 #
27 # 第一个转义符把第2个\转义了,但是第3个又变成"裸体的"了,
28 #+ 与上边的例子的原因相同
29
30 variable=\\\\
31 echo "$variable" # \\
32 # 转了两个\
33 # 没问题
################################End Script#########################################
转义一个空格,在命令行参数列表中将会阻止单词分隔问题.
################################Start Script#######################################
1 file_list="/bin/cat /bin/gzip /bin/more /usr/bin/less /usr/bin/emacs-20.7"
2 # 列出的文件都作为命令的参数.
3
4 # Add two files to the list, and list all.
4 # 加2个文件到list中,并且列出全部.
5 ls -l /usr/X11R6/bin/xsetroot /sbin/dump $file_list
6
7 echo "-------------------------------------------------------------------------"
8
9 # 如果我们转义2个空格,会发生什么?
10 ls -l /usr/X11R6/bin/xsetroot\ /sbin/dump\ $file_list
11 # 错误: 因为前3个路径名被合并成一个参数传给了'ls -l'
12 # 因为2个转义符阻止了参数(单词)分离
################################End Script#########################################
转义符也提供续行功能.一般,每一行都包含一个不同的命令,但如果在行尾加上\,那就会接受
新行的输入,作为这一行的补充.
1 (cd /source/directory && tar cf - . ) | \
2 (cd /dest/directory && tar xpvf -)
3 # 重复了 Alan Cox的目录树拷贝命令
4 # 为了增加可读性分成2行.
5
6 # 也可以使用如下方式:
7 tar cf - -C /source/directory . |
8 tar xpvf - -C /dest/directory
9 # 察看下边的注意事项
注意:如果一个脚本以|(管道字符)结束.那么一个\(转义符),就不用非加上不可了.
但是一个好的shell脚本编写风格,还是应该在行尾加上\,以增加可读性.
################################Start Script#######################################
1 echo "foo
2 bar"
3 #foo
4 #bar
5
6 echo
7
8 echo 'foo
9 bar' # 没区别
10 #foo
11 #bar
12
13 echo
14
15 echo foo\
16 bar # 续行
17 #foobar
18
19 echo
20
21 echo "foo\
22 bar" # 与上边一样,\还是作为续行符
23 #foobar
24
25 echo
26
27 echo 'foo\
28 bar' # 由于是强引用,所以\没被解释成续行符
29 #foo\
30 #bar
################################End Script#########################################