一、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条)
总结的很好,态度很端正,加油。
@马哥教育:好的,谢谢