条件判断是一个程序获得智能的基础,而Bourne Shell脚本则通过命令 [ 来模拟大多数编程语言中的条件表达式。
shell中支持的控制结构有:
(1) if then else fi
(2) for in do done
(3) while do done
这些关键字可以引导程序检查状态码 $?,从而实现控制。下面讲解这个命令如何模拟条件表达式。
文件/目录判断
[ -b FILE ] 如果 FILE 存在且是一个块特殊文件则为真。
[ -c FILE ] 如果 FILE 存在且是一个字特殊文件则为真。
[ -d DIR ] 如果 FILE 存在且是一个目录则为真。
[ -e FILE ] 如果 FILE 存在则为真。
[ -f FILE ] 如果 FILE 存在且是一个普通文件则为真。
[ -g FILE ] 如果 FILE 存在且已经设置了SGID则为真。
[ -k FILE ] 如果 FILE 存在且已经设置了粘制位则为真。
[ -p FILE ] 如果 FILE 存在且是一个名字管道(F如果O)则为真。
[ -r FILE ] 如果 FILE 存在且是可读的则为真。
[ -s FILE ] 如果 FILE 存在且大小不为0则为真。
[ -t FD ] 如果文件描述符 FD 打开且指向一个终端则为真。
[ -u FILE ] 如果 FILE 存在且设置了SUID (set user ID)则为真。
[ -w FILE ] 如果 FILE存在且是可写的则为真。
[ -x FILE ] 如果 FILE 存在且是可执行的则为真。
[ -O FILE ] 如果 FILE 存在且属有效用户ID则为真。
[ -G FILE ] 如果 FILE 存在且属有效用户组则为真。
[ -L FILE ] 如果 FILE 存在且是一个符号连接则为真。
[ -N FILE ] 如果 FILE 存在 and has been mod如果ied since it was last read则为真。
[ -S FILE ] 如果 FILE 存在且是一个套接字则为真。
[ FILE1 -nt FILE2 ] 如果 FILE1 has been changed more recently than FILE2, or 如果 FILE1 exists and FILE2 does not则为真。
[ FILE1 -ot FILE2 ] 如果 FILE1 比 FILE2 要老, 或者 FILE2 存在且 FILE1 不存在则为真。
[ FILE1 -ef FILE2 ] 如果 FILE1 和 FILE2 指向相同的设备和节点号则为真。
字符串判断
[ -z STRING ] 如果STRING的长度为零则为真 ,即判断是否为空,空即是真;
[ -n STRING ] 如果STRING的长度非零则为真 ,即判断是否为非空,非空即是真;
[ STRING1 = STRING2 ] 如果两个字符串相同则为真 ;
[ STRING1 != STRING2 ] 如果字符串不相同则为真 ;
[ STRING1 ] 如果字符串不为空则为真,与-n类似
数值判断
INT1 -eq INT2 INT1和INT2两数相等为真 ,=
INT1 -ne INT2 INT1和INT2两数不等为真 ,<>
INT1 -gt INT2 INT1大于INT1为真 ,>
INT1 -ge INT2 INT1大于等于INT2为真,>=
INT1 -lt INT2 INT1小于INT2为真 ,<
INT1 -le INT2 INT1小于等于INT2为真,<=
总之,“=”用于比较字符串;“-eq”用于比较整型数
复杂逻辑判断
-a 与
-o 或
! 非
1、如果a>b且a<c
if (( a > b )) && (( a < c ))
if [[ $a > $b ]] && [[ $a < $c ]]
if [ $a -gt $b -a $a -lt $c ]
2、如果a>b或a<c
if (( a > b )) || (( a < c ))
if [[ $a > $b ]] || [[ $a < $c ]]
if [ $a -gt $b -o $a -lt $c ]
"||"和"&&"在SHELL里可以用,第一个可以写成if [ a>b && a ]
双括号和双中括号的含义见下文中对各种括号的解释。
Shell中的括号(), (()), [], [[]]
1、单小括号 ()
①命令组。括号中的命令将会新开一个子shell顺序执行,所以括号中的变量不能够被脚本余下的部分使用。括号中多个命令之间用分号隔开,最后一个命令可以没有分号,各命令和括号之间不必有空格。
②命令替换。等同于`cmd`,shell扫描一遍命令行,发现了$(cmd)结构,便将$(cmd)中的cmd执行一次,得到其标准输出,再将此输出放到原来命令。注意花括号${}也可以被看做命令替换,但是两者有所区别,花括号内是定义好的变量,而不是shell命令。
③用于初始化数组。如:array=(a b c d)
2、双小括号 (( ))
①整数扩展。这种扩展计算是整数型的计算,不支持浮点型。((exp))结构扩展并计算一个算术表达式的值,如果表达式的结果为0,那么返回的退出状态码为1,或者 是"假",而一个非零值的表达式所返回的退出状态码将为0,或者是"真"。
②只要括号中的运算符、表达式符合C语言运算规则,都可用在$((exp))中。作不同进位(如二进制、八进制、十六进制)运算时,输出结果全都自动转化成了十进制。如:echo $((16#5f)) 结果为95 (16进位转十进制)。单纯用 (( )) 也可重定义变量值,比如 a=5; ((a++)) 可将 $a 重定义为6。双括号中的变量可以不使用$符号前缀。括号内支持多个表达式用逗号分开。
③甚至可以替代循环结构,只要括号中的表达式符合C语言运算规则。比如可以直接使用for((i=0;i<5;i++)), 如果不使用双括号, 则为for i in `seq 0 4`或者for i in {0..4}。再如可以直接使用if (($i<5)), 如果不使用双括号, 则为if [ $i -lt 5 ]。
3、单中括号 []
①标准命令 [ 和 test 是具有相同作用,区别在于 [ 命令的最后一个参数是 "]" 。
②在正则表达式中用于表示字符范围。作为test用途的中括号内不能使用正则。
④在一个array 结构的上下文中,中括号用来引用数组中每个元素的编号。
4、双中括号[[ ]]
①[[是 bash 程序语言的关键字,并不是一个命令。[[ ]] 结构比[ ]结构更加通用,不过注意[[ 依然要和中括号内的内容用空格隔开。
②支持字符串的模式匹配,使用=~操作符时支持正则表达式,使用==操作符时则支持通配符,字符串比较时可以把右边的作为一个模式,而不仅仅是一个字符串,比如[[ hello == hell? ]],结果为真。
③使用[[ ... ]]条件判断结构,而不是[ ... ],能够防止脚本中的许多逻辑错误。比如,&&、||、<和> 操作符能够正常存在于[[ ]]条件判断结构中,但是如果出现在[ ]结构中的话,会报错,原因是 [ 命令不能解释&&的含义。比如可以直接使用if [[ $a != 1 && $a != 2 ]], 如果使用单括号, 则为if [ $a -ne 1] && [ $a != 2 ]或者if [ $a -ne 1 -a $a != 2 ]。
④bash把双中括号中的表达式看作一个单独的元素,并返回一个退出状态码。
语法提醒:shell有时靠命令来模拟运算。例如四则运算可以借助:let, expr 等命令完成。如对变量 x 加 1 可以写作:let "x = $x + 1" 或者 x=`expr $x + 1‘ 。如果参数有空格,一定要用引号括起来才不会出错。空格在shell中是有意义的。
PS:注意shell中,所有的输入都是字符串类型(当然任何语言都是如此,只是shell支持的类型解释和转换比较少),其中美元符$ 反引号` 反斜线 都被sh进程或者bash进程解释成字符串替换或命令替换,也就是用特定的输出字符串替换原命令,然后再继续执行新的命令。
调试:
查看shell脚本的执行过程
在执行的时候,通过下面的方式:
#sh -x test.sh
获取命令行参数:
注意 $ 是一个被解释成命令替换的符号,和反引号有类似之处。
$# 是传给脚本的单词个数
$0 是脚本本身的名字
$1 是传递给该shell脚本的第一个单词
$2 是传递给该shell脚本的第二个单词
$@ 是传给脚本的所有单词组成的列表数据结构
$* 将被替换成一个被包括在引号内的字符串,以一个单字符串显示所有向脚本传递的参数,与位置变量不同,参数可超过9个
$$ 是脚本运行的当前进程ID号
$? 是显示最后命令的退出状态,0表示没有错误,其他表示有错误
shell文本处理
重点掌握三个命令:find, grep, sed, awk,也就实现了对文本的增(插入)、删、改(替换)、查
1、find命令用于查找文件,如:
xin$ sudo find / -type f -size +1G
-name filename #查找名为filename的文件
用于查找磁盘上大于1G的文件。
2、grep命令用于查找文本,格式为:
grep <pattern> <filename>
3、sed命令用于编辑文本文件,最基本的命令当然是插入和删除,格式为:
sed [options] 'command' file(s) 注意用单引号而不是双引号, $ ` 等才不会被shell解释成字符串替换标志,而会直接把字面值转交给sed解释。
options:
-i :直接修改读取的文件内容,而不是将程序的结果输出到终端。
command:
格式 [n1,[n2]] function 其中 n1 和 n2 表示目标文本的首行行号和尾行行号
function:
a :新增, a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)
c :取代, c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行
d :删除,因为是删除啊,所以 d 后面通常不接任何字符串
i :插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行)
p :列印,亦即将某个选择的数据印出。通常 p 会与参数 sed -n 一起运行
s :替换,可以直接进行取代的工作,通常这个 s 的动作可以搭配正规表示法,例如 1,20s/old/new/g
$:代表最后一行
示例:
替换:
Linux格式为:sed -i "s/192.168.0.2/192.168.0.3/g" *.rptdesign
Mac格式为:sed -i "" "s/192.168.0.2/192.168.0.3/g" *.rptdesign 相当于需要一个备份文件
删除:
sed -i "" '/^abc/d' ~/tmp/tmp.txt
插入:
sed -i "" '/192/a
abc
' ~/tmp/tmp.txt
管道
示例: $ find . | xargs svn add --force *.*
xargs命令会把从管道中获得的输入拆分,逐个作为下一个命令的输入。