这本电子书在电脑里已经有段时间了,虽然平时写点shell没问题,但是感觉还是不精,就拿这本书查漏补缺吧.
注: 脚本例子大部分引用原书
就从12章开始吧
cat , tac , rev
cat test tac test rev test 123 789 321 456 456 654 789 123 987
expr
通用求值表达式: 通过给定的操作(参数必须以空格分开)连接参数, 并对参数求值. 可以使算术操作, 比较操作, 字符串操作或者是逻辑操作. expr 3 + 5 返回8 expr 5 % 3 返回2 expr 1 / 0 返回错误消息, expr: division by zero 不允许非法的算术操作. expr 5 \* 3 返回15 在算术表达式expr中使用乘法操作时, 乘法符号必须被转义. y=`expr $y + 1` 增加变量的值, 与let y=y+1和y=$(($y+1))的效果相同. 这是使用算术表达式的一个例子. z=`expr substr $string $position $length` 在位置$position上提取$length长度的子串.
date
# date --date='1 days ago' 2011年 05月 06日 星期五 21:04:58 CST # date --date='1 days ago' +%Y%m%d 20110506
join
join命令只能够操作两个文件. 它可以将那些具有特定标记域(通常是一个数字标签)的行合并起来, 并且将结果输出到stdout. 被加入的文件应该事先根据标记域进行排序以便于能够正确的匹配.
1 File: 1.data 2 3 100 Shoes 4 200 Laces 5 300 Socks ------------------------------ 1 File: 2.data 2 3 100 $40.00 4 200 $1.00 5 300 $2.00 ------------------------------ bash$ join 1.data 2.data File: 1.data 2.data 100 Shoes $40.00 200 Laces $1.00 300 Socks $2.00
[root@bash]# mktemp -p /home/ abc.XXXXX /home/abc.V2113
[root@bash ~]# for num in $(seq 10);do echo $num;done 1 2 3 4 5 6 7 8 9 10
bash$ wall System going down for maintenance in 5 minutes! Broadcast message from bozo (pts/1) Sun Jul 8 13:53:27 2001... System going down for maintenance in 5 minutes!
使用后置引用的算术扩展(通常都是和expr一起使用) z=`expr $z + 3` # 'expr'命令将会执行这个扩展. 后置引用形式的算术扩展已经被双括号形式所替代了 -- ((...))和$((...)) -- 当然也可以使用非常方便的let结构. 1 z=$(($z+3)) 2 z=$((z+3)) # 也正确. 3 # 使用双括号的形式, 4 #+ 参数解引用 5 #+ 是可选的. 6 7 # $((EXPRESSION))是算数表达式. # 不要与命令替换 8 #+ 相混淆. 9 10 11 12 # 使用双括号的形式也可以不用给变量赋值. 13 14 n=0 15 echo "n = $n" # n = 0 16 17 (( n += 1 )) # 递增. 18 # (( $n += 1 )) is incorrect! 19 echo "n = $n" # n = 1 20 21 22 let z=z+3 23 let "z += 3" # 使用引用的形式, 允许在变量赋值的时候存在空格. 24 # 'let'命令事实上执行得的是算术赋值, 25 #+ 而不是算术扩展.
1 stringZ=abcABC123ABCabc 2 3 echo ${#stringZ} # 15 4 echo `expr length $stringZ` # 15 5 echo `expr "$stringZ" : '.*'` # 15
1 stringZ=abcABC123ABCabc 2 # 0123456789..... 3 # 0-based indexing. 4 5 echo ${stringZ:0} # abcABC123ABCabc 6 echo ${stringZ:1} # bcABC123ABCabc 7 echo ${stringZ:7} # 23ABCabc 8 9 echo ${stringZ:7:3} # 23A 10 # 提取子串长度为3. 11 12 13 14 # 能不能从字符串的右边(也就是结尾)部分开始提取子串? 15 16 echo ${stringZ:-4} # abcABC123ABCabc 17 # 默认是提取整个字符串, 就象${parameter:-default}一样. 18 # 然而 . . . 19 20 echo ${stringZ:(-4)} # Cabc 21 echo ${stringZ: -4} # Cabc 22 # 这样, 它就可以工作了. 23 # 使用圆括号或者添加一个空格可以"转义"这个位置参数.
1 stringZ=abcABC123ABCabc 2 # |----| 3 # |----------| 4 5 echo ${stringZ#a*C} # 123ABCabc 6 # 截掉'a'到'C'之间最短的匹配字符串. 7 8 echo ${stringZ##a*C} # abc 9 # 截掉'a'到'C'之间最长的匹配字符串.
1 stringZ=abcABC123ABCabc 2 # || 3 # |------------| 4 5 echo ${stringZ%b*c} # abcABC123ABCa 6 # 从$stringZ的结尾位置截掉'b'到'c'之间最短的匹配. 7 8 echo ${stringZ%%b*c} # a 9 # 从$stringZ的结尾位置截掉'b'到'c'之间最长的匹配.
1 stringZ=abcABC123ABCabc 2 3 echo ${stringZ/abc/xyz} # xyzABC123ABCabc 4 # 使用'xyz'来替换第一个匹配的'abc'. 5 6 echo ${stringZ//abc/xyz} # xyzABC123ABCxyz 7 # 用'xyz'来替换所有匹配的'abc'.
1 stringZ=abcABC123ABCabc 2 3 echo ${stringZ/#abc/XYZ} # XYZABC123ABCabc 4 # 用'XYZ'替换开头的'abc'. 5 6 echo ${stringZ/%abc/XYZ} # abcABC123ABCXYZ 7 # 用'XYZ'替换结尾的'abc'.
printf
[root@x1 ~]# for num in $(seq 10);do printf '%02d\n' $num ;done 01 02 03 04 05 06 07 08 09 10 注意: printf '%02d\n' (这之间必须有空格)$num
正则表达式 re
grep -E '\<22\>|\<15\>' secure
转义的"尖括号" -- \<...\> -- 用于匹配单词边界.
尖括号必须被转义才含有特殊的含义, 否则它就表示尖括号的字面含义.
"\<the\>" 完整匹配单词"the", 不会匹配"them", "there", "other", 等等.
扩展的正则表达式
-
问号 -- ? -- 匹配它前面的字符, 但是只能匹配1次或0次. 通常用来匹配单个字符.
-
加号 -- + -- 匹配它前面的字符, 能够匹配一次或多次. 与前面讲的*号作用类似, 但是不能匹配0个字符的情况.
- 转义"大括号" -- \{ \} -- 在转义后的大括号中加上一个数字, 这个数字就是它前面的RE所能匹配的次数.大括号必须经过转义, 否则, 大括号仅仅表示字面含意.
-
圆括号 -- ( ) -- 括起一组正则表达式. 当你想使用expr进行子字符串提取(substring extraction)的时候, 圆括号就有用了. 如果和下面要讲的"|"操作符结合使用, 也非常有用.
- 竖线 -- | -- 就是RE中的"或"操作符, 使用它能够匹配一组可选字符中的任意一个.
POSIX字符类. [:class:]
-
[:alnum:] 匹配字母和数字. 等价于A-Za-z0-9.
-
[:alpha:] 匹配字母. 等价于A-Za-z.
-
[:blank:] 匹配一个空格或是一个制表符(tab).
-
[:cntrl:] 匹配控制字符.
-
[:digit:] 匹配(十进制)数字. 等价于0-9.
-
[:graph:] (可打印的图形字符). 匹配ASCII码值范围在33 - 126之间的字符. 与下面所提到的[:print:]类似, 但是不包括空格字符(空格字符的ASCII码是32).
-
[:lower:] 匹配小写字母. 等价于a-z.
-
[:print:] (可打印的图形字符). 匹配ASCII码值范围在32 - 126之间的字符. 与上边的[:graph:]类似, 但是包含空格.
-
[:space:] 匹配空白字符(空格和水平制表符).
-
[:upper:] 匹配大写字母. 等价于A-Z.
-
[:xdigit:] 匹配16进制数字. 等价于0-9A-Fa-f.
22. 进程替换
>(command)
<(command)
启动进程替换. 它使用/dev/fd/<n>文件将圆括号中的进程处理结果发送给另一个进程. [1] (译者注: 实际上现代的UNIX类操作系统提供的/dev/fd/n文件是与文件描述符相关的, 整数n指的就是进程运行时对应数字的文件描述符)
进程替换可以比较两个不同命令的输出, 甚至能够比较同一个命令不同选项情况下的输出.
bash$ comm <(ls -l) <(ls -al) total 12 -rw-rw-r-- 1 bozo bozo 78 Mar 10 12:58 File0 -rw-rw-r-- 1 bozo bozo 42 Mar 10 12:58 File2 -rw-rw-r-- 1 bozo bozo 103 Mar 10 12:58 t2.sh total 20 drwxrwxrwx 2 bozo bozo 4096 Mar 10 18:10 . drwx------ 72 bozo bozo 4096 Mar 10 17:58 .. -rw-rw-r-- 1 bozo bozo 78 Mar 10 12:58 File0 -rw-rw-r-- 1 bozo bozo 42 Mar 10 12:58 File2 -rw-rw-r-- 1 bozo bozo 103 Mar 10 12:58 t2.sh
为了让函数可以返回字符串或是数组, 可以使用一个在函数外可见的专用全局变量.
在函数被调用之前, 所有在函数中声明的变量, 在函数体外都是不可见的, 当然也包括那些被明确声明为local的变量.
1 count_lines_in_etc_passwd() 2 { 3 [[ -r /etc/passwd ]] && REPLY=$(echo $(wc -l < /etc/passwd)) 4 # 如果/etc/passwd是可读的, 那么就把REPLY设置为文件的行数. 5 # 这样就可以同时返回参数值与状态信息. 6 # 'echo'看上去没什么用, 可是 . . . 7 #+ 它的作用是删除输出中的多余空白字符. 8 } 9 10 if count_lines_in_etc_passwd 11 then 12 echo "There are $REPLY lines in /etc/passwd." 13 else 14 echo "Cannot count lines in /etc/passwd." 15 fi
26. 数组
Bash支持一维数组.
声明数组
array[10]= #初始化一个空数组元素 array=(1 2 3 4 5) #有5个元素
declare -a array #也是声明一个数组
echo ${array[1]} #调用数组元素,下标从0开始计算
array_name=([xx]=XXX [yy]=YYY ...)
area3=([17]=seventeen [24]=twenty-four) #另一种初始化并赋值的方法
echo ${array[@]} #打印数组所有元素
echo ${array[*]} #同上
打印一首小诗
Line[1]="I do not know which to prefer," Line[2]="The beauty of inflections" Line[3]="Or the beauty of innuendoes," Line[4]="The blackbird whistling" Line[5]="Or just after." for index in 1 2 3 4 5 # 5行. do printf " %s\n" "${Line[index]}" done
数组操作
array=( one two three four five five ) echo ${#array[0]} 和 echo ${#array} #第一个数组元素长度 echo ${#array[*]} 和 echo ${#array[@]} #数组元素个数 #提取数组子串 echo ${arrayZ[@]:0} # one two three four five five # 所有元素. echo ${arrayZ[@]:1} # two three four five five # element[0]后边的所有元素. echo ${arrayZ[@]:1:2} # two three # 只提取element[0]后边的两个元素.
bash使用C语言语法的for循环
#!/bin/bash for ((i=0;i<6;i++)); do echo $i done #结果 0 1 2 3 4 5
29. 调试
sh -n scriptname不会运行脚本, 只会检查脚本的语法错误.
sh -v scriptname将会在运行脚本之前, 打印出每一个命令.
sh -x scriptname会打印出每个命令执行的结果, 但只使用缩写形式.
#在大括号包含的代码块中, 最后一条命令没有以分号结尾. 1 { ls -l; df; echo "Done." } 2 # bash: syntax error: unexpected end of file 3 4 { ls -l; df; echo "Done."; } 5 # ^ ### 最后的这条命令必须以分号结尾.