一、select循环
功能:主要用于创建菜单,菜单按数字顺序排列。并将PS3变量的值用作用户输入提示。用户的选择被保存在内置变量REPLY中。也可以和case语句结合,在select循环中对用户的输入作出判断并处理。
注意:select循环为无限循环,因此需要给出循环退出条件。
例1:让用户选择其来自哪个省市
#!/bin/bash # echo "where are you come from" PS3="i came from :" declare -a cm=('beijing' 'shanghai' 'hainan' 'hebei' 'hunan') select path in ${cm[@]} do break done echo "you are from $path"
例2:询问用户是哪个足球俱乐部的球迷,并显示该俱乐部的教练。当用户输入quit时,退出。
#!/bin/bash # echo "Which football club is your favorite(quit to exit):" PS3="my favorite is : " select path in 'ManUnited' 'Barcelona' 'Chelsea' 'RealMadrid' 'quit' do case $path in ManUnited) echo "you are $path fans" echo "$path coah is Jose mourinho" ;; Barcelona) echo "you are $path fans" echo "$path coah is Enrique" ;; Chelsea) echo "you are $path fans" echo "$path coah is Conti" ;; RealMadrid) echo "you are $path fans" echo "$path coah is Zidane" ;; quit) echo "See you..." exit ;; *) echo "Unknown option." continue ;; esac done
二、函数
介绍:函数是由若干条shell命令组成的语句块,实现代码重用和模块化编程。
应用场景:在编写shell脚本时,有些代码会被反复重用多次,把这些可能重复使用的代码写成函数,这样就可以通过函数名调用来高效、重复地利用。另外,当shell脚本功能比较复杂时,可以将脚本划分为多个模块,不同模块可以写成不同的函数。
2.1函数的语法格式:
格式1、
f_name () {
函数体…
}
格式2、
function f_name {
函数体…
}
2.2函数的生命周期:被调用时创建,返回时终止。
2.3函数的调用:
函数要能被调用,需在调用前进行定义。调用时直接键入函数名即可。例:
若不调用函数,则函数不会被执行。
2.4函数的返回值:
分为两种
a、正常返回的数值:
(1)函数中的打印语句,如echo、print
(2)函数中命令的执行结果
示例1:函数执行成功之后,会将用户输入的指令输出
如何获取函数的输出。有时候我们需要对函数正常输出的数值进行处理,这时可以通过下面的方式获取函数的输出
示例2:将函数执行之后的输出保存,进行处理
b、执行状态返回值:
(1)取决于函数中执行的最后一条语句的执行状态
(2)也可通过return自定义
return N(0-255)
在示例3中,可以观察到,函数的执行状态取决于函数中执行的最后一条语句的执行状态;但是没办法判断函数中的其他语句是否执行成功,当需要根据函数中不同的语句的执行状态做出判断;来决定后续的操作时,需要用到return。如示例4。
#!/bin/bash # user_input () { read -p "please enter an excuteable command : " cmd echo $cmd ls /etc/justfortest } user_input echo "Funtion status is $?"
在示例4中,需要判断用户输入的用户是否存在,存在时,返回5的状态值;用户不存在时,添加用户,根据添加用户的命令的执行状态来判断添加是否成功。
2.5在脚本中调用函数:
在一个脚本中想要调用写在另一个脚本中的函数,同过source ”脚本名“来实现
cacufun.sh脚本内容如下:cacufun.sh定义了加减乘除4中运算的函数
#!/bin/bash # addtion_num() { local res=`echo "$1 $2 $3"| bc` echo "result is $res" } subtract_num() { local res=`echo "$1 $2 $3"| bc` echo "result is $res" } multiplication_num() { local res=`echo "$1 $2 $3"| bc` echo "result is $res" } devision_num() { local res=`echo "$1 $2 $3"| bc` echo "result is $res" }
在cacutest.sh脚本中调用cacufun.sh脚本中的函数,实现对用户输入数据的运算。
#!/bin/bash # source /root/bin/practice/cacufun.sh read -p "enter two int number to caculate: " num1 num2 num3 case $num2 in +) echo "$num1 $2 $num3 " `addtion_num $num1 $num2 $num3` ;; -) echo "$num1 $2 $num3 " `substract_num $num1 $num2 $num3` ;; *) echo "$num1 $2 $num3 " `multiplication_num $num1 $num2 $num3` ;; /) cho "$num1 $2 $num3 " `devision_num $num1 $num2 $num3` ;; *) echo "wrong option" exit 123 esac
2.6函数参数:
函数可以使用类似于调用脚本位置参数来调用函数的参数
函数的参数表示:
$1,$2
$#
$@,$*
函数中的$1,$2…指的是传递给函数的参数,而不是传递个脚本的参数
2.7 shell脚本中的变量作用域:
1、在函数中使用了在主程序中声明的变量,重新赋值会直接修改主程序中的变量。
2、如果想让函数中的变量与主程序中的变量冲突,在函数中声明变量时,使用local修饰,将变量声明为局部变量,其作用域仅限于当前函数。
3、变量被声明的位置决定了其作用域,
3.1 在函数中使用了在主程序中没有声明的变量,在函数执行结束后即被撤销,无论是否使用local修饰符
3.2 在函数中声明了主程序中声明过的变量,这两个变量为不同的变量。函数中的变量在执行结束后即被撤销。
2.8 shell脚本中变量的查找顺序:
1、内层函数本身
2、外层还是定义的
3、主程序
4、shell环境变量
2.9 函数的递归:
函数直接或调用自身
示例:
斐波那契数列又称黄金分割数列,因数学家列昂纳多· 斐波那契以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:0 、1、1 、2 、3 、5 、8 、13 、21 、34 、…… ,斐波纳契数列以如下被以递归的方法定义:F (0 )=0 ,F (1 )=1 ,F (n )=F(n-1)+F(n-2) (n≥2) )
写一个函数,求n阶斐波那契数列
#!/bin/bash # fab () { if [[ $1 -eq 1 || $1 -eq 2 ]];then echo -n "1 " else echo -n "$[$(fab $[$1-1])+$(fab $[$1-2])] " fi } for i in $(seq 1 $1) do fab $i done echo
三、实战演练
1、编写服务脚本/root/bin/testsrv.sh,完成如下要求
(1) 脚本可接受参数:start, stop, restart, status
(2) 如果参数非此四者之一,提示使用格式后报错退出
(3) 如是start:则创建/var/lock/subsys/SCRIPT_NAME, 并显示“启动成功”
考虑:如果事先已经启动过一次,该如何处理?
(4) 如是stop:则删除/var/lock/subsys/SCRIPT_NAME, 并显示“停止完成”
考虑:如果事先已然停止过了,该如何处理?
(5) 如是restart,则先stop, 再start
考虑:如果本来没有start,如何处理?
(6) 如是status, 则如果/var/lock/subsys/SCRIPT_NAME文件存在,则显示“SCRIPT_NAMEis running…”
如果/var/lock/subsys/SCRIPT_NAME文件不存在,则显示“SCRIPT_NAME is stopped…”
其中:SCRIPT_NAME为当前脚本名
#!/bin/bash # srv_file=/var/lock/subsys/$0 srv_name=`basename $0` usage_srv() { if [[ $1 -lt 1 || $1 -ge 2 ]];then echo "Wrong option:" echo "Usage : $srv_name start|stop|restart|status" exit 123 fi } start_srv() { if [ -f $srv_file ];then echo "service $srv_name already started" else touch $srv_file && echo "start $srv_name finished" fi } stop_srv() { if [ ! -f $srv_file ];then echo "service $srv_name already stopped" else rm -f $srv_file &>/dev/null && echo "stop $srv_name finished" fi } status_srv() { if [ -f $srv_file ];then echo "service $srv_name is running" else echo "service $srv_name was stopped" fi } case $1 in start) start_srv $1 ;; stop) stop_srv $1 ;; restart) stop_srv $1 start_srv $1 ;; status) status_srv $1 ;; *) usage_srv $# ;; esac
2、编写脚本/root/bin/copycmd.sh
(1) 提示用户输入一个可执行命令名称;
(2) 获取此命令所依赖到的所有库文件列表
(3) 复制命令至某目标目录(例如/mnt/sysroot)下的对应路径下;
如:/bin/bash ==> /mnt/sysroot/bin/bash
/usr/bin/passwd==> /mnt/sysroot/usr/bin/passwd
(4) 复制此命令依赖到的所有库文件至目标目录下的对应路径下:
如:/lib64/ld-linux-x86-64.so.2 ==> /mnt/sysroot/lib64/ld-linux-x86-64.so.2
(5)每次复制完成一个命令后,不要退出,而是提示用户键入新的要复制的命令,并重复完成上述功能;
直到用户输入quit退出
#!/bin/bash # f_dir=/mnt/sysroot cmd_input () { while true do read -p "Please input an excuteable command to backup (quit to exit): " cmd if [[ -z $cmd ]];then echo "wrong option,try again..." continue elif [[ $cmd == "quit" ]];then exit 88 elif ! which --skip-alias $cmd &>/dev/null;then echo "No such command,try again..." continue else break fi done } cp_cmd() { cmd_path=`which --skip-alias $cmd` cmd_dir=`dirname $cmd_path` [ ! -d $f_dir$cmd_dir ] && mkdir -p $f_dir$cmd_dir cp $cmd_path $f_dir$cmd_dir && echo "backup $cmd finished" } cp_cmd_lib() { cmd_lib=`ldd $cmd_path` for path in $cmd_lib do cmd_lib_dir=`echo $path | grep -o -E '/[^[:space:]]+.*/'` if [[ ! -d $f_dir$cmd_lib_dir ]];then mkdir -p $f_dir$cmd_lib_dir && cp $path $f_dir$cmd_lib_dir &> /dev/null else cp $path $f_dir$cmd_lib_dir &> /dev/null fi done echo "backup ${cmd}'s library finished" } main() { while true do cmd_input cp_cmd cp_cmd_lib done } main
原创文章,作者:M20-1钟明波,如若转载,请注明出处:http://www.178linux.com/38011
评论列表(2条)
总结的很好,态度很端正,加油。
@马哥教育:好的,谢谢