函数的作用就是将程序里多次被调用的相同代码组合起来(函数体),并为其取一个名字,即函数名。其他所有想重复调用这部分代码的地方都只需要调用这个名字就可以了。当需要修改这部分代码时候,只需要修改函数体内的这部分代码即可实现对所有调用的修改,也可以把函数独立地写在文件里,当需要调用函数时,再加载进来使用。
好处:
1)把相同的程序段定义成函数,可以减少程序代码量;
2)增加程序的可读性,易读性;
3)呈现程序的功能模块化,使得程序具备通用性(可移植性)。
语法:
function 函数名() {指令...return n
}函数名() {指令...return n
}
执行:
1)执行不带参数的函数,直接输入函数名即可。
2)执行带参数的函数:
函数名 参数1 参数2
3)函数的执行必须要在执行的程序前面定义和加载。
4)Shell执行系统中各种程序的执行顺序:系统别名-》函数-》系统命令-》可执行文件。
5)函数执行的时候,会和调用它的脚本公用变量,也可以为函数设定局部变量及特殊位置参数。
6)函数体中的return语句会返回一个退出值(返回值)给调用函数的当前程序。
7)如果将函数存放在一个独立的文件中,被脚本加载使用的时候,需要使用source或者点号来加载。
8)在函数内一般使用local定义局部变量,这些变量离开函数后,就会消失。
9)Shell的位置参数($1/$2/$#/$*/$?/$@)都可以作为函数的参数来使用。
10)此时的父脚本的参数临时地被函数参数所掩盖或者隐藏。
11)$0 比较特殊,他仍然是父脚本的名称。
12)当函数执行完成时,原来的命令行脚本的参数即可恢复。
13)函数的参数变量是在函数体里面定义的。
示例1:
[root@vm1 scripts]# cat function01.sh
#!/bin/bashchang(){echo "I am chang."
}chun() {echo "I am chun."
}chang # 调用chang
chun #调用chun
[root@vm1 scripts]# sh function01.sh
I am chang.
I am chun.
示例2:
分离函数体和执行函数的脚本文件(更规范的方法)
首先是建立函数体脚本(默认不会执行函数)。
使用cat命令追加多行脚本,以将函数代码追加到系统的函数文件中,即/etc/init.d/functions。
注意的是:要将定义的函数放在return 0的上面。如果放在return 0语句的下面,将无法调用到oldboy函数。在/etc/init.d/functions系统函数文件中添加的函数将不起作用。
然后在编辑脚本:
[root@vm1 scripts]# cat function02.sh
#!/bin/bash
#
[ -f /etc/init.d/functions ] && . /etc/init.d/functions || exit 1oldboy
[root@vm1 scripts]# sh function02.sh
I am oldboy
程序和函数分离的常见方法:
[root@vm1 scripts]# cat fun02.sh
#!/bin/bash# 在函数文件中只定义函数
# 可以在该函数文件中定义更多的函数
fun_01(){echo "I am oldboy linux."
}[root@vm1 scripts]# cat exec01.sh
#!/bin/bash
#
[ -f fun02.sh ] && . ./fun02.sh || exit 1# 执行函数
fun_01[root@vm1 scripts]# sh exec01.sh
I am oldboy linux.
说明:
1)在函数文件中可以定义多个函数。
2)我们可以创建单独的函数文件,然后加载这个函数文件,而不需要使用系统的函数文件/etc/init.d/functions。
3)这是一个比较规范的方法。
示例3:带参数的shell函数。
[root@vm1 scripts]# cat fun02.sh
#!/bin/bash# 在函数文件中只定义函数
# 可以在该函数文件中定义更多的函数
fun_01(){echo "I am oldboy linux."
}# 带参数的函数
fun_02(){echo "I am $1"
}[root@vm1 scripts]# cat exec01.sh
#!/bin/bash
#
[ -f fun02.sh ] && . ./fun02.sh || exit 1# 执行带参数的函数,这个$1本身是脚本的位置参数,但是$1占据的位置为函数的参数位置,现在它们重合了。
fun_02 $1# 将changchunhua作为参数
[root@vm1 scripts]# sh exec01.sh changchunhua
I am changchunhua
说明:
此时的父脚本的参数临时地被函数参数所掩盖或者隐藏。
案例:利用shell函数开发企业级URL检测脚本:
[root@vm1 scripts]# cat url_check.sh
#!/bin/bashif [ $# -ne 1 ]thenecho "USAGE: $0 URL"exit 1
fiwget --spider --timeout 10 --tries=2 $1 &>/dev/nullif [ $? -eq 0 ]thenecho "$1 is yes."
elseecho "$1 is no."
fi
测试结果:
[root@vm1 scripts]# sh url_check.sh www.baidu.com
www.baidu.com is yes.[root@vm1 scripts]# sh url_check.sh jd.com
jd.com is yes.
然后,我们再进行一次升级:
将检测的功能写成函数,并将函数传参转换成脚本命令行传参,判断任意指定的url是否存在异常。
[root@vm1 scripts]# cat url_check2.sh
#!/bin/bash# 帮助函数
function usage(){echo "USAGE: $0 url"exit 1
}# 检测URL函数
function check_url(){wget --spider --timeout 10 --tries=2 $1 &>/dev/nullif [ $? -eq 0 ]thenecho "$1 is yes."elseecho "$1 is no."fi
}# 主函数
function main(){if [ $# -ne 1 ] # 如果传入的是多个参数,则打印帮助函数,提示用户。thenusageficheck_url $1
}main $* # 这里的$*就是把命令行接受的所有参数作为函数参数传给函数的内部,是一种常用的手法
学习了函数以后,尽量将脚本功能模块化,每个模块实现一个功能,并且让脚本可以通用。
工作中使用的一些Shell脚本,有时间也是需要进行升级修改,将脚本功能模块化。
然后我们使用更加专业的显示输出:
[root@vm1 scripts]# cat url_check3.sh
#!/bin/bash[ -f /etc/init.d/functions ] && . /etc/init.d/functions || exit 1# 帮助函数
function usage(){echo "USAGE: $0 url"exit 1
}# 检测URL函数
function check_url(){wget --spider --timeout 10 --tries=2 $1 &>/dev/nullif [ $? -eq 0 ]then# echo "$1 is yes."action "$1 is yes." /bin/trueelse# echo "$1 is no."action "$1 is no." /bin/truefi
}function main(){if [ $# -ne 1 ]thenusageficheck_url $1
}main $* # 这里的$*就是把命令行接受的所有参数作为函数参数传给函数的内部,是一种常用的手法
执行结果:
[root@vm1 scripts]# sh url_check3.sh jd.com
jd.com is yes. [ OK ]