写在前面:
博客书写牢记5W1H法则:What,Why,When,Where,Who,How。
本篇主要内容:
● case语句
● function函数
● 数组
● bash内置字符串处理
● glob模式匹配扩展
● 脚本信号捕捉
● bash中使用ASCII颜色
● dialog实现窗口化编程
case语句
case $WORD in
pattern1 [| pattern1_1] ... )
list1
;;
pattern2 [| pattern2_1] ... )
list2
;;
* )
default
;;
esac
补充:case语句支持glob通配符:
*:任意长度任意字符;
?:任意单个字符;
[]:范围内任意单个字符;
a|b:a或b;
function函数
语法一:
function f_name {
...函数体...
}
语法二:
f_name() {
...函数体...
}
意义:
代码重用,模块化、结构化编程。
特点:
调用执行,但需要定义被调用语句前部,否则可能无法被调用。建议将函数放到脚本首部。
生命周期:
每次被调用时创建、返回终止。
运行状态返回值:
最后一条命令的执行结果;
return #重定义的值;
运行返回值:
函数体内各种形式的输出语句。
函数位置参数:
函数可以接收位置参数变量,在函数调用时在函数名后面直接跟参数即可。
在函数内使用$1,$2...引用函数位置参数。
在函数内使用$*或$@引用所有参数
在函数内使用$#引用参数个数。
局部变量:
作用域为函数生命周期,在函数结束时被自动销毁。
定义方法:
local VAR=VALUE
函数递归:
函数直接或间接的调用自身。如:
# 求10!。已知10!=10*9!=10*9*8!=... fact () { if [ $1 -eq 0 -o $1 -eq 1 ];then echo 1 else fact $[ $1 * $(fact $[$1-a]) ] fi } fact $1
注意:函数递归会占用大量内存空间,影响性能,不建议使用。
#!/bin/bash #求$1的阶乘。已知10!=10*9!=10*9*8!... 注意:0!=1 1!=1 num=$1 declare -i res=1 for i in `seq 0 $num`;do if [ $i -eq 0 ];then res=1 elif [ $i -eq 1 ];then res=1 else let res*=$i fi done echo "${1}! = $res"
数组:
变量是存储单个元素内存空间;而数组是存储多个元素的连续内存空间。
数组名:
整个数组诸多元素共有一个数组名。
数组索引:
引用数组中各个元素的值:数组名[索引],索引从0开始
注意:bash-4.x及以后版本支持自定义索引格式,而不必仅仅是数字,此种数字称为“关联数组”,如:
week[sun]="sunday" week[we]="wendy"
声明:
declare -a ARR:声明索引数组;
declare -A ARR:声明关联数组;
数组赋值:
(1)单个元素赋值:
ARR[INDEX]=VALUE
ARR=([0]="VALUE0" [5]="VALUE4" ...)
(2)为整个数组赋值:
ARR=("VALUE0" "VALUE1" ...)
(3)使用read命令赋值
read -a ARR
各数组元素会以空格为分隔符。
数组元素引用:
${ARR[INDEX]}:引用第INDEX+1个数组元素
${ARR[@]}:引用数组中所有元素
${ARR[*]}:引用数组中所有元素
${ARR[@]:OFFSET:NUM} 跳过前OFFSET个元素,取出其后的NUM个元素
${ARR[@]:OFFSET} 跳过前OFFSET个元素,取出其后所有元素
数组长度:
${#ARR[@]}
${#ARR[*]}
列出数组索引:
${!ARR[@]}
${!ARR[#]}
删除数组中元素:
unset ARR[INDEX]
数组中追加元素:
ARR[${#ARR[@]}]=VALUE
bash内置字符串处理:
(详细参考man bash的Parameter Expansion章节,1000行左右)
变量长度:
${#VAR} :取$VAR的字符串长度
${#ARR[INDEX]} :取数组ARR的索引为INDEX的元素值的字符串长度
${#ARR[@]} :取数组ARR的元素个数
${#ARR[*]} :取数组ARR的元素个数
[root@www test]# str="How are you?" [root@www test]# declare -a arr=(1 2 3 4 5) [root@www test]# echo ${#str} 12 [root@www test]# echo ${#arr[*]} 5 [root@www test]# echo ${#arr[@]} 5 [root@www test]# echo ${#arr[0]} 1
变量字符串切片:
${VAR:OFFSET} :$VAR中,跳过前OFFSET个字符,取其后的所有字符
${VAR:OFFSET:LENGTH} :$VAR中,跳过前OFFSET个字符,取其后的LENGTH个
${VAR: -LENGTH} :$VAR中,取最后LENGTH个字符(注意空格)
[root@www test]# echo $str How are you? [root@www test]# echo ${str:4} are you? [root@www test]# echo ${str:4:3} are [root@www test]# echo ${str: -4} you?
基于模式取变量中字符:
PATTERN仅支持glob形式模式匹配
${VAR#PATTERN}:$VAR中,从左到右,最短匹配PATTERN,取剩余部分
${VAR##PATTERN}:$VAR中,从左到右,最长匹配PATTERN,取剩余部分
${VAR%PATTERN}:$VAR中,从右向左,最短匹配PATTERN,取剩余部分
${VAR%%PATTERN}:$VAR中,从右向左,最长匹配PATTERN,取剩余部分
[root@www test]# file="/usr/bin/ls" [root@www test]# echo ${file#*/} usr/bin/ls [root@www test]# echo ${file##*/} ls [root@www test]# echo ${file%/*} /usr/bin [root@www test]# echo ${file%%/*}
变量判断赋值:
${parameter:-word}:返回默认值。当$parameter为空或未定义,返回word;否则返回$parameter;
${parameter:=word}:设置默认值。当$parameter为空或未定义,设置parameter的值为word并返回;否则返回$parameter;
${parameter:?word}:返回错误信息。当$parameter为空或未定义,将word以标准错误返回;否则返回$parameter;
${parameter:+word}:替换返回值。当$parameter为空或未定义,返回空;否则,返回word;
#返回默认值 [root@www test]# unset file [root@www test]# echo ${file:-"/usr/bin/ls"} /usr/bin/ls [root@www test]# echo $file #设置默认值 [root@www test]# unset file [root@www test]# echo ${file:="/usr/bin/ls"} /usr/bin/ls [root@www test]# echo $file /usr/bin/ls [root@www test]# echo ${file:="/sbin/ss"} /usr/bin/ls [root@www test]# echo $file /usr/bin/ls #返回错误信息 [root@www test]# unset file [root@www test]# echo ${file:?"file is unset"} -bash: file: file is unset [root@www test]# echo $? 1 [root@www test]# file="/sbin/ss" [root@www test]# echo ${file:?"file is unset"} /sbin/ss #替换返回值 [root@www test]# unset file [root@www test]# echo ${file:+"/sbin/ss"} [root@www test]# file="/usr/bin/ls" [root@www test]# echo ${file:+"/sbin/ss"} /sbin/ss [root@www test]# echo $file /usr/bin/ls
变量字符串替换与删除:
${VAR/PATTERN} :$VAR中,匹配模式PATTERN,将第一个匹配到的字符删除
${VAR/PATTERN/STRING} :$VAR中,匹配模式PATTERN,将第一个匹配到的字符替换为STRING
${VAR//PATTERN} :$VAR中,匹配模式PATTERN,将所有匹配到的字符删除
${VAR//PATTERN/STRING} :$VAR中,匹配模式PATTERN,将所有匹配到的字符替换为STRING
${VAR/#PATTERN} :$VAR中,锚定行首,匹配模式PATTERN,将第一个匹配到的字符删除
${VAR/#PATTERN/STRING} :$VAR中,锚定行首,匹配模式PATTERN,将第一个匹配到的字符替换为STRING
${VAR/%PATTERN} :$VAR中,锚定行尾,匹配模式PATTERN,将第一个匹配到的字符删除
${VAR/%PATTERN/STRING} :$VAR中,锚定行尾,匹配模式PATTERN,将第一个匹配到的字符替换为STRING
[root@www test]# echo $file /usr/bin/ls [root@www test]# echo ${file/\/usr/} /bin/ls [root@www test]# echo ${file/\/usr\//\/s} /sbin/ls [root@www test]# echo ${file/%ls/cp} /usr/bin/cp
字符串大小写转换:
${VAR^^}:把VAR中的所有小写字符转换为大写;
${VAR,,}:把VAR中的所有大写字符转换为小写;
拓展:
${VAR^PATTERN}:第一个被PATTERN到的字符转换为大写;
${VAR^^PATTERN}:所有被PATTERN到的字符转换为大写;
${VAR,PATTERN}:第一个被PATTERN到的字符转换为小写;
${VAR,,PATTERN}:所有被PATTERN到的字符转换为小写;
[root@www test]# echo $file /usr/bin/ls [root@www test]# echo ${file^^} /USR/BIN/LS [root@www test]# file=${file^^} [root@www test]# echo $file /USR/BIN/LS [root@www test]# echo ${file,,} /usr/bin/ls
glob模式匹配扩展:
pattern-list可以使用“|”分隔多个
?(pattern-list) :pattern-list出现0次或一次
*(pattern-list) :pattern-list出现0次或多次
+(pattern-list) :pattern-list出现1次或多次
@(pattern-list) :pattern-list内任意元素出现一次(注意这里的各元素是以"|"分隔的!区别于[] )
!(pattern-list) :排除pattern-list内元素外的其他元素
实例:
[root@www test]# touch {,file,filefile}{,10,20,0} [root@www test]# ls 0 10 20 file file0 file10 file20 filefile filefile0 filefile10 filefile20 [root@www test]# ls ?(file)10 10 file10 [root@www test]# ls ?(file|1)0 0 10 file0 [root@www test]# ls +(file)0 file0 filefile0 [root@www test]# ls *(file)0 0 file0 filefile0 #与[]不同的是,()中的元素会被当做一个整体! [root@www test]# touch 120 [root@www test]# ls @(12)0 120 [root@www test]# ls @(file|1|2)0 10 20 file0 [root@www test]# ls !(file)10 10 filefile10
信号捕捉:
列出信号:
trap -l
kill -l
man 7 signal
设置信号捕捉及处理:
trap "COMMAND或FUNCTIONZ_NAME" SIGNALS 设置捕捉到信号后执行的命令或函数
常用被捕捉的信号:
HUP,INT
实例:
#睡眠30秒,在睡眠期间如果用户按了Ctrl+C则显示本目录内容
[root@www shell]# cat trap #!/bin/bash # trap "ls" INT sleep 30 [root@www shell]# ./trap ^C0.sh days.sh
#ping 172.18.X.1,X为1-255,若能ping通则返回up,否则返回down,脚本运行期间,用户键入Ctrl+C则退出脚本
[root@www shell]# cat ping.sh #!/bin/bash # trap "user_exit" INT user_exit () { echo -e "\b\b >>> Script stopd. <<< " exit 1 } IP_HEAD="172.18." IP="" for (( i=1; i<=255; i++ ));do IP="${IP_HEAD}${i}.1" ping -W 1 -c 1 $IP &> /dev/null && echo " $IP is up." || echo " $IP is down." done [root@www shell]# ./ping.sh 172.18.1.1 is up. 172.18.2.1 is down. >>> Script stopd. <<<
bash中使用ACSII颜色:
\033[31mHELLO WROLD\033[0m
输出HELLO WORLD
\033[42;31;5mHELLO WORLD\033[0m
输出HELLO WORLD并闪烁
含义:
\033[ 为固定字符,表示要设置颜色信息
##m
第1个#,设置为3表示前景色,4为背景色
第2个#,颜色类别
1,2,3,4,5,6,7
#m
加粗、闪烁等功能
补充:各项之间以“;”分隔
详细参考:http://www.jb51.net/article/48844.htm
dialog
命令可实现窗口化编程;
各窗体控件使用方式;
如何获取用户选择或键入的内容?
默认,其输出信息被定向到了错误输出流;
实例:
写一个脚本,完成如下功能
(1) 提示用户输入一个可执行命令的名称;
(2) 获取此命令所依赖到的所有库文件列表;
(3) 复制命令至某目标目录(例如/mnt/sysroot,即把此目录当作根)下的对应的路径中
bash, /bin/bash ==> /mnt/sysroot/bin/bash
useradd, /usr/sbin/useradd ==> /mnt/sysroot/usr/sbin/useradd
(4) 复制此命令依赖到的所有库文件至目标目录下的对应路径下;
/lib64/ld-linux-x8664.so.2 ==> /mnt/sysroot/lib64/ld-linux-x8664.so.2
进一步:
每次复制完成一个命令后,不要退出,而是提示用户继续输入要复制的其它命令,并重复完成如上所描述的功能;直到用户输入“quit”退出脚本;
[root@www shell]# cat sysroot.sh #!/bin/bash # 说明:用户给出一个命令,将命令文件和命令所依赖的函数库拷贝到指定目录,这里设置为/mnt/sysroot # exit 4: command not found #定义用户命令变量comm,生成临时文件,设置并创建目标目录 declare comm="" sysroot="/mnt/sysroot/" mkdir -p /mnt/sysroot &> /dev/null script_name=`basename $0` workingdir="`pwd`/" tempfile_ldd_print=`mktemp ${script_name}.tempfile.XXXXX` tempfile_ldd_des=`mktemp ${script_name}.tempfile.XXXXX` #定义函数,将用户命令传值过来进行处理,完成cp命令和库文件操作 cp_command () { local com="$1" local com_path=`which $com --skip-alias` #获取命令cp到的目的路径,并判断是否存在,不存在,则添加 local com_des_dir="$sysroot`dirname $com_path | cut -c 2-`" ! [ -d $com_des_dir ] && mkdir -p $com_des_dir && echo "$com_dis_dir not exist, created." cp -p $com_path $com_des_dir && echo " -- copy $com_path ==> $com_des_dir" #将ldd的输出过滤出包含路径的行并写入到临时文件,等待进一步处理 ldd $com_path | grep '/' > $tempfile_ldd_print #循环语句,对ldd输出的临时文件的每一行进行处理 > $tempfile_ldd_des while read line;do #去除行中/前的字符,写入到新的文件 echo "${line#*/}" >> $tempfile_ldd_des done < $tempfile_ldd_print #循环语句,对上一步处理得到的临时文件继续处理,以得到路径,cp库文件到指定目录 #由于得到的路径首部/被去除,所以需要先cd到/ cd / while read line;do #去除(后面的内容,得到的内容即为库文件路径,写入数组 lib_path=${line%(*} des_file="$sysroot$lib_path" #得到lib文件cp到的目的路径,检查是否存在,不存在则建立 des_path=`dirname $des_file` ! [ -d $des_path ] && mkdir -p $des_path && echo "$des_path not exist, created." cp -f $lib_path $des_path && echo " -- copy /$lib_path ==> $des_path" done < $workingdir$tempfile_ldd_des echo "-->>> SUCCESS! <<<-- \"$comm\" command copy complite." echo "---------------------------------------" } #定义函数,清除创建的临时文件 clear_tempfile () { rm -f $workingdir$tempfile_ldd_print rm -f $workingdir$tempfile_ldd_des } #循环提示用户输入命令,并完成操作,直到用户输入quit退出 while true;do read -p "Please input command [\"quit\" to exit]: " comm echo $comm | grep "[[:space:]]" > /dev/null && echo " >>> Only 1 args shoud be gaven,if there is a space in your command,please use \"\"." && continue [[ "$comm" == "quit" ]] && clear_tempfile && exit 0 ! which $comm --skip-alias &> /dev/null && echo " >>> command \"$comm\" not found,please check" && continue cp_command $comm done [root@www shell]# ./sysroot.sh Please input command ["quit" to exit]: cp -- copy /usr/bin/cp ==> /mnt/sysroot/usr/bin -- copy /lib64/libselinux.so.1 ==> /mnt/sysroot/lib64 -- copy /lib64/libacl.so.1 ==> /mnt/sysroot/lib64 -- copy /lib64/libattr.so.1 ==> /mnt/sysroot/lib64 -- copy /lib64/libc.so.6 ==> /mnt/sysroot/lib64 -- copy /lib64/libpcre.so.1 ==> /mnt/sysroot/lib64 -- copy /lib64/liblzma.so.5 ==> /mnt/sysroot/lib64 -- copy /lib64/libdl.so.2 ==> /mnt/sysroot/lib64 -- copy /lib64/ld-linux-x86-64.so.2 ==> /mnt/sysroot/lib64 -- copy /lib64/libpthread.so.0 ==> /mnt/sysroot/lib64 -->>> SUCCESS! <<<-- "cp" command copy complite. --------------------------------------- Please input command ["quit" to exit]: sdlkjf >>> command "sdlkjf" not found,please check Please input command ["quit" to exit]: cp ls mv >>> Only 1 args shoud be gaven,if there is a space in your command,please use "". Please input command ["quit" to exit]: quit [root@www shell]#