shell扩展是个大话题,主要有以下几种扩展:
1、brace expansion(花括号扩展);
2、tilde expansion(代字符号扩展);
3、parameter expansion(参量扩展);
4、variable expansion(变量扩展);
5、command substitution(命令替换);
6、arithmetic expansion(算术扩展);
7、process substitution(过程替换);
8、word splitting(词分离);
9、file name expansion(路径名扩展)。
以上九种扩展是按顺序进行的,首先进行brace expansion,最后进行file expansion。下面按顺序分别介绍各种扩展的用法。
一、brace expansion
关于brace expansion,首先要和参量扩展、变量扩展区分,看下面的输出:
wangjk@wangjiankun:~$ cat expansion.sh
1 #!/bin/bash
2
3 echo "brace expansion"
4 echo sp{el,il,al}l
5
6 echo
7 echo "variable expansion"
8 firstname=wang
9 echo ${firstname}jiankun
wangjk@wangjiankun:~$ ./expansion.sh
brace expansion
spell spill spallvariable expansion
wangjiankun
wangjk@wangjiankun:~$关于brace expansion要注意花括号中不允许出现不加引号的空格,如有不加引号的空格将不进行扩展,如下所示:
wangjk@wangjiankun:~$ cat expansion1.sh
1 #!/bin/bash
2
3 echo sp{el,il, al}l
4 echo sp{el,il," al"}l
5 echo sp{el,il," "al}l
wangjk@wangjiankun:~$ ./expansion1.sh
sp{el,il, al}l
spell spill sp all
spell spill sp all
wangjk@wangjiankun:~$
二、tilde expansion
首先解释一个单词,
tilde
1 : a mark ~ placed especially over the letter n to denote the sound /ny/ or over vowels to indicate nasality
2 : the mark tilde used to indicate negation in logic and the geometric relation “is similar to” in mathematics总之,就是符号“~”。
关于~,我们很常用,比如将目录切换到某用户(例如:root)的家目录就可以这样做:cd ~root,如下所示:
wangjk@wangjiankun:~$ pwd
/home/wangjk
wangjk@wangjiankun:~$ cd ~root
wangjk@wangjiankun:/root$ pwd
/root
wangjk@wangjiankun:/root$ cd ~wangjk
wangjk@wangjiankun:~$ pwd
/home/wangjk
wangjk@wangjiankun:~$关于tilde expansion,我们只知道当它后跟用户名时,代表是该用户的家目录,当它后跟以“/”开头的路径时,代表是当前用户的家目录就可以了,也就是说,它的默认值是当前用户的家目录,如下所示:
wangjk@wangjiankun:~$ pwd
/home/wangjk
wangjk@wangjiankun:~$ whoami
wangjk
wangjk@wangjiankun:~$ cd ~root
wangjk@wangjiankun:/root$ pwd
/root
wangjk@wangjiankun:/root$ cd ~/scripts
wangjk@wangjiankun:~/scripts$ pwd
/home/wangjk/scripts
wangjk@wangjiankun:~/scripts$ cd ~root/123
wangjk@wangjiankun:/root/123$ pwd
/root/123
wangjk@wangjiankun:/root/123$注:它还有“~+”、“~-”的用法,这里不再介绍,其实很简单,试一下便知。
三、shell parameter and variable expansion
说到参量和变量扩展,我们必须介绍两个符号“$”和“{}”,“$”不但用于参量和变量扩展中,而且用于命令替换、算术扩展,它是参量、变量、命令、算术扩展的“引导符”。
在参数扩展(位置参数小于10)和变量扩展中,“{}”不是必要的,但是它可以起到保护变量的作用,其保护作用表现在变量名后是字符的情况,如下所示:
wangjk@wangjiankun:~$ cat expansion.sh
1 #!/bin/bash
2
3 firstname=wang
4 echo ${firstname}jiankun
5 echo $firstnamejiankun
wangjk@wangjiankun:~$ ./expansion.sh
wangjiankunwangjk@wangjiankun:~$
第五行的输出没有达到我们的目的,因为bash认为变量名为:firstnamejiankun,bash并不会自动识别变量名。在这种情况下,必须用“{}”。
在这里我再介绍一个间接扩展(indirct expansion)的例子,间接扩展要用到符号:“!”,输出如下所示:
wangjk@wangjiankun:~$ cat expansion.sh
1 #!/bin/bash
2
3 wang=Wang
4 firstname=wang
5 echo ${!firstname}jiankun
wangjk@wangjiankun:~$ ./expansion.sh
Wangjiankun
wangjk@wangjiankun:~$这里,由于“!”的存在,bash将变量firstname的值wang又作为变量名,最终扩展的结果是:Wang,得到Wangjiankun。
关于参数、变量扩展,我再介绍最后一个话题:就是如下形式的扩展:${VAR:=variable},先看下面的输出:
wangjk@wangjiankun:~$ cat expansion.sh
1 #!/bin/bash
2
3 firstname=wang
4 echo ${firstname:=WANG}jiankun
5 echo ${name:=WANG}jiankun
wangjk@wangjiankun:~$ ./expansion.sh
wangjiankun
WANGjiankun
wangjk@wangjiankun:~$如果变量已定义,不进行赋值操作,直接按原定义扩展;如果变量没有定义先赋值再扩展。
四、command substitution
命令替换,注意,是替换(substitution)!而不是执行!看下面的输出:
wangjk@wangjiankun:~$ cat command.sh
1 #!/bin/bash
2
3 echo
4 date
5
6 echo
7 $(date)
wangjk@wangjiankun:~$ bash -x command.sh
+ echo+ date
Wed Jul 15 16:42:25 EDT 2009
+ echo++ date
+ Wed Jul 15 16:42:25 EDT 2009
command.sh: line 7: Wed: command not found
wangjk@wangjiankun:~$ ./command.shWed Jul 15 16:42:31 EDT 2009
./command.sh: line 7: Wed: command not found
wangjk@wangjiankun:~$上面脚本的第七行虽然报错了,但不是命令替换出错了,相反,命令替换是成功的,但是bash将替换后的星期三:Wed看做是命令,所以导致了出错。这就是替换:bash将$()中的单词看做是命令,先将其执行,将执行后的输出原封不动的放在原地,任由bash处理。上面的Wed由于它在一行的行首,所以bash认为它是命令。
命令替换有且只有两种格式:
$(command)
`command`
这里要区分一下brace和parentheses,及花括号和小括号。前者用于变量和参量扩展,而后者用于命令替换,bash将$(word)中的word当做命令来执行。
五、arithmetic expansion
算术扩展的语法格式:$((EXPRESSION))
在算术扩展中有一个重要的点,就是在双小括号中可以进行参数扩展、命令替换、以及引号的移除。英文原文:All tokens in the expression undergo parameter expansion, command substitution, and quote removal. 看下面的输出:
wangjk@wangjiankun:~$ cat command.sh
1 #!/bin/bash
2
3 echo
4 val=3
5
6 echo
7 echo $(($1+val+2+`date +%m`))
wangjk@wangjiankun:~$ bash -x command.sh
+ echo+ val=3
+ echo++ date +%m
+ echo 12
12
wangjk@wangjiankun:~$ bash -x command.sh 2
+ echo+ val=3
+ echo++ date +%m
+ echo 14
14
wangjk@wangjiankun:~$第一次没有指定位置参数,所以输出为12(变量val=3,date命令输出的月份为7,再加上常量2);第二次指定位置参数$1为2,所以输出为14。
bash算术扩展的运算符和优先级与C语言中极其相似,这里不再介绍,必要时可以查看手册页。
bash的变量可以作为操作数出现在算术扩展的表达式中。
对于常量,以0(zero)开头表示是八进制数,以0x(0X)开头表示是十六进制数。bash支持2到64之间的任意进制数,关于它们如何表示,不详细介绍,只给出如下输出:
wangjk@wangjiankun:~$ cat command.sh
1 #!/bin/bash
2
3 echo "octal numbers"
4 echo "011==$((011))"
5
6 echo
7 echo "hexadecimal numbers"
8 echo "0x1a==$((0x1a))"
9
10 echo
11 echo "arithmetic base 36"
12 echo "36#z==$((36#z))"
13 echo "36#Z==$((36#Z))"
14
15 echo
16 echo "arithmetic base 37"
17 echo "37#a==$((37#a))"
18 echo "37#A==$((37#A))"
19
20 echo
21 echo "arithmetic base 64"
22 echo "64#@==$((64#@))"
23 echo "64#_==$((64#_))"wangjk@wangjiankun:~$ ./command.sh
octal numbers
011==9hexadecimal numbers
0x1a==26arithmetic base 36
36#z==35
36#Z==35arithmetic base 37
37#a==10
37#A==36arithmetic base 64
64#@==62
64#_==63
wangjk@wangjiankun:~$
六、process substitution
过程替换是很难说清楚的一种替换,它与重定向也很难区分!如果不深究原理,表面的现象是:重定向是一口入,一口出;而process substitution可以多口入(事实上,它根本就没有“入”,不要被它的两个带尖的符号给迷惑了!)。
下面,我要通过例子把process substitution说清楚。先看下面的输出:
wangjk@wangjiankun:~$ ls -l j.txt
-rw-r--r-- 1 wangjk wangjk 0 Jul 3 19:25 j.txt
wangjk@wangjiankun:~$ ls -l <(true)
lr-x------ 1 wangjk wangjk 64 Jul 20 18:55 /dev/fd/63 -> pipe:[6193]
wangjk@wangjiankun:~$以上两个命令的输出形式是一样的,都是列出了一个文件的信息,我们可不可以先这样类比:第二个命令中<(true)就是/dev/fd/63?
运行命令:wangjk@wangjiankun:~$ vim <(ls -l)得到下图所示的输出:
从上图中vim的最底下一行的显示可以看出这个文件的名字是:/dev/fd/63,看来<(true)还真是/dev/fd/63,这样我们就可以对process substitution中的substitution(替换)有一个全新的认识:<(LIST)或>(LIST)就是一个文件名。下面的输出可以说明这一点:
wangjk@wangjiankun:~$ ls -l <(true) >(true)
l-wx------ 1 wangjk wangjk 64 Jul 20 19:30 /dev/fd/62 -> pipe:[6466]
lr-x------ 1 wangjk wangjk 64 Jul 20 19:30 /dev/fd/63 -> pipe:[6464]
wangjk@wangjiankun:~$到此为止,我们清楚了所谓的process substitution的<(LIST)和>(LIST)就是一个用文件描述符表示的文件,那么<(LIST)和>(LIST)到底有什么区别呢?一时间,我没有想到合适的例子来解释二者的区别,现将某资料的英文原文摘录如下:
If the ">(LIST)" form is used, writing to the file will provide input for LIST. If the "<(LIST)" form is used, the file passed as an
argument should be read to obtain the output of LIST.
七、word splitting
如果参数扩展、命令替换、算术扩展没有在双引号中,shell将在其结果中进行词分离操作。
shell把$IFS中的每一个字符看做是分隔符,并且将其他扩展的结果在出现这些字符处进行分离。如果$IFS没有设置,默认情况下,它的值是"'<space><tab><newline>'",此时任何IFS字符序列都可以分离单词。关于词分离先只介绍这些,在今后的脚本和命令中遇到时,在逐个分析。
八、file name expansion