本章学习内容
----------函数介绍
----------函数定义
----------函数使用
----------区分return和exit
----------删除函数
----------注意事项
1、介绍函数
编写复杂的shell脚本时,完成具体任务的代码可能需要重复使用,此时我们可以将这些代码块剥离出来,并赋予名称,称其为函数。那么,我们在使用这样的代码块时,只需引用该代码块被赋予的函数名称(成为函数调用)。
2、定义函数
<1>俩种方式
▲function fname
{
command
command
}
▲fname
{
command
command
}
<2>函数名不能和命令相同,命令优先级高于函数
<3>函数命名规则
不可与命令相同,否则命令无法正常使用。
函数优先级>命令
不可定义与函数名同名的别名,否则函数无法使用。
别名优先级>函数
所以优先级:别名 > 函数 > 命令
<4>函数中的变量设为局部变量,防止与shell的变量冲突
验证
####创建函数文件fun2,定义test函数 [[email protected] ~]#vim fun2 function test() { a=first echo "a=$a" } ####编写脚本testfun2.sh [[email protected] ~]#vim testfun2.sh #!/bin/bash # a=second source fun2 test echo "a=$a" ####执行脚本 [[email protected] ~]#bash testfun2.sh a=first a=first
结果表明虽然脚本中定义了a=second,但是执行test函数之后,已经变成了first。为了解决脚本中变量和函数中变量的混淆,一般将函数中的变量定义为局部变量。
3、使用函数
<1>载入函数
子shell中如果需要使用父shell中的函数,需要将函数加载至本shell
加载方式
source FUNCTION
. FUNCTION
注:修改函数之后,必须重新载入shell才能生效
<2>调用函数
输入函数名再加参数即可
4、函数返回值return和exit的不同
return:退出当前函数
return :从函数中返回,用最后状态命令决定返回值
return 0 :无错误返回。
return 1-255 :有错误返回
exit:退出当前脚本
5、删除函数
unset FUNCTION:删除函数
set:查看所有定义的函数
6、关于函数参数传递问题
不知道大家有没有过这样的疑问,在函数中参数是如何传递的呢?因为在C语言中是这样定义一个函数的:int cmp(int a, int b),变量已经在函数中声明。但是shell中的函数并没有定义参数,这个过程是怎么完成的呢?小编刚开始也很疑惑,后来才明白shell的函数中通过函数位置变量和变量的引用就可以实现参数的传递。下面是一些示例以及传参的注意事项,大家可以参考一下。
####创建函数文件fun1,定义函数add function add() { local sum sum=$[$1+$2] echo $sum } ####编写脚本testfun1.sh #!/bin/bash source fun1 add $1 $2 ####执行脚本: [[email protected] ~/bin]#bash funadd.sh 1 2 3
脚本中的$1和$2是脚本引用命令行的位置变量,而函数中的$1和$2是引用脚本中的第一个变量和第二个变量,我们称其为函数位置变量。(实现传参的方法之一)
看下面这种情况
####创建函数文件fun1,定义函数add function add() { local sum sum=$[$1+$2] echo $sum } ####编写脚本testfun1.sh #!/bin/bash source fun1 read -p "Please input two figures:" a b #####只添加此行 add $1 $2 ####执行脚本 [[email protected] ~/bin]#bash funadd.sh Please input two figures:1 2 /root/bin/fun1: line 3: +: syntax error: operand expected (error token is "+")
为什么这里会报错呢?因为在脚本中引用的位置变量$1和$2并不是1和2,也就是说$1和$2并没有值,函数中并没有引用到数值,计算结果当然是错的。从侧面也说明了read是无法引用位置变量的。
那么,该怎么解决呢?答案如下
#!/bin/bash source fun1 read -p "Please input two figures:" a b add $a $b [[email protected] ~/bin]#bash funadd.sh Please input two figures:1 2 3
因为此时的$a和$b引用了命令行输入的1和2,而函数中又会调用这俩个值,结果计算正确。
再看一种情况
####创建函数文件fun1,定义函数add function add() { local sum sum=$[$a+$b] echo $sum } ####编写脚本testfun1.sh #!/bin/bash source fun1 add $1 $2 或者 add $a $b 或者 add ####执行脚本 [[email protected] ~/bin]#bash funadd.sh Please input two figures:1 2 3
小编已经实验过,这三种情况都能运行出结果,为了书写简便,都一一列举再在此处。但是大家知道这是为什么吗?因为函数直接引用了命令行的参数(实现传参的方法之二,弱类型编程语言特有的变量引用),也就不需要通过脚本来传参了。所以如果脚本中没有使用$1和$2之类的字符来引用变量,而是直接使用了$a和$b之类的字符,那是此时传参的意义也就不存在了。
再举一个例子加深一下印象
####创建函数文件fun3,定义函数string function string() { if [ $1 == ha ]; then echo "nihao" fi } ####编写脚本testfun3.sh #!/bin/bash # source fun3 a=ha string $a unset a ####执行脚本 [[email protected] ~]#bash testfun3.sh nihao
####创建函数文件fun3,定义函数string function string() { if [ $a == ha ]; then echo "nihao" fi } ####编写脚本testfun3.sh #!/bin/bash # source fun3 a=ha string $a 或者 string $1 或者 string unset a ####执行脚本 [[email protected] ~]#bash testfun3.sh nihao
课后强化练习
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为当前脚本名
####创建函数文件testsrv,定义多个函数 [[email protected] ~/bin/dir]#vim testsrv function start() { if [ -e /var/lock/subsys/testsrv.sh ]; then echo "Already start..." else touch /var/lock/subsys/testsrv.sh echo "start ok" fi } function stop() { if [ ! -e /var/lock/subsys/testsrv.sh ]; then echo "Already stop..." else rm -f /var/lock/subsys/testsrv.sh echo "stop ok" fi } function restart() { stop stop start start } function status() { if [ -e /var/lock/subsys/testsrv.sh ]; then echo "testsrv.sh is running..." else echo "testsrv.sh is stopped..." fi } function quit() { exit 2 } function again() { while [ $1 != start -a $1 != stop -a $1 != restart -a $1 != status ]; do read -p "Error,please enter again:" CHOICE done } ####最后一个函数未使用,问题。 ####编写脚本testsrv3.sh source /root/bin/dir/testsrv cat << EOF four choices for you: start) stop) restart) status) EOF read -p "please input your choice(start|stop|restart|status|quit):" CHOICE while [ $CHOICE != "start" -a $CHOICE != "stop" -a $CHOICE != "restart" -a $CHOICE != "status" ]; do read -p "Error,please enter again:" CHOICE done case $CHOICE in start) start ;; stop) stop ;; restart) restart ;; status) status ;; quit) quit ;; esac
或者使用select
####函数不变,改写脚本 #!/bin/bash source testsrv PS3="please input your choice:" select CHOICE in start stop restart status quit; do case $CHOICE in start) start ;; stop) stop ;; restart) restart ;; status) status ;; quit) quit ;; *) echo "Error,please enter again..." esac done
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退出
####创建函数文件copycmd,定义多个函数 function query() { ldd /usr/bin/$1 } function copy() { mkdir /mnt/sysroot &> /dev/null local dir1 dir1=/mnt/sysroot cp /usr/bin/$1 $dir1 mkdir /mnt/sysroot/lib64 &> /dev/null local dir2 dir2=/mnt/sysroot/lib64 cp `ldd /usr/bin/ls | sed -r ‘[email protected][[:space:]]+.*=>?[[:space:]]?(.*)[[:space:]].*@\[email protected]‘` $dir2 &> /dev/null } function quit() { if [ $1 == quit ]; then exit fi } ####编写脚本copycmd4 #!/bin/bash source copycmd PS3=‘Please input your option:‘ select option in run quit; do ####列出俩个选项,是否运行或者退出,select自带循环功能 case $option in 退出即可,option是run和quit run) read -p "Input your cmd:" CMD ####赋初值,否则无法与quit比较,直接错误 until [[ $CMD == quit ]]; do if which $CMD &> /dev/null; then query $CMD ####调用函数 copy $CMD ####调用函数 else read -p "Error,again:" CMD fi read -p "Input your cmd:" CMD ####纠正初值,与quit比较 done ;; quit) quit $option ;; esac done