在过程式编程中,代码会重用。过程式编程又分为:模块化编程和结构化编程。
把代码重用的代码段进行命名,并重复调用,这就是函数。
函数(function):把一段独立功能的代码当作一个整体,并为之命名一个名字,成为命名的代码段,此即为函数。
在使用函数时,需要注意以下几点:
函数常用的语法格式有两种:
function f_name {
...函数体...
}
f_name() {
...函数体...
}
函数的生命周期在被调用时开始,返回时终止。
其状态返回结果为函数体运行的最后一条命令的状态结果。当然也可以自定义状态返回结果。
# return [0-255] ;其中 0 代表成功; 1~255 代表失败。
大家一定要注意区分状态返回结果和状态返回值:
命令的返回结果:比如使用 ls 输出的内容列表
命令的状态返回值: 使用 # echo $? 进行查看。0 代表成功; 1~255 代表失败。
函数的返回值包括两个:
a、使用 echo 或者 printf 命令进行输出(printf 不会换行)
b、函数体中调用的命令的执行结果
a、默认取决于函数体中执行的最后一条命令的退出状态码
b、自定义:return 值
函数可以接受函数,在函数体当中,可以使用 $1,$2,… 引用传递给函数的参数。也可以使用 $* 或 $@ 引用所有的参数,使用 $# 引用传递的参数的个数。
在调用函数时,在函数后面以空白符分隔给定参数列表即可,例如: testfunc arg1 arg2 arg3 …
但是一定要注意,脚本参数 和 函数参数 是两回事,可以通过以下示例进行了解。
1、添加10个用户,添加用户的功能使用函数实现,用户名作为参数传递给参数。
[root@LeeMumu ~]# bash userAdd10.sh JC
Add user JC1 finished.
Add user JC2 finished.
Add user JC3 finished.
Add user JC4 finished.
Add user JC5 finished.
Add user JC6 finished.
Add user JC7 finished.
Add user JC8 finished.
Add user JC9 finished.
Add user JC10 finished.
[root@LeeMumu ~]# cat userAdd10.sh
#!/bin/bash
#
# 5:user exists
addusers() {
if id $1 &> /dev/null; then
return 5
else
useradd $1
retval=$?
return $retval
fi
}
for i in {1..10}; do
addusers ${1}${i} # ${1}${i} 在这里对于函数 addusers 来说就是位置参数 $1
retval=$?
if [ $retval -eq 0 ]; then
echo "Add user ${1}${i} finished."
elif [ $retval -eq 5 ]; then
echo "user ${1}${i} exists."
else
echo "Unkown Error."
fi
done
2、编写一个脚本,查看 192.168.1.1~192.168.1.254 之间哪些地址可以ping通。
[root@LeeMumu ~]# bash pING.SH
Ping 192.168.1.1 successfully!
Ping 192.168.1.2 successfully!
... ...
[root@LeeMumu ~]# cat pING.SH
#!/bin/bash
#
#
pingtest() {
if ping $1 -c 1 &> /dev/null ; then
echo "Ping $1 successfully!"
else
echo "Ping $1 unsuccessfully!"
fi
}
for i in {1..254}; do
pingtest 192.168.1.$i # 192.168.1.$i 在这里对于函数 pingtest 来说就是位置参数 $1
done
3、打印NN乘法表。
[root@LeeMumu ~]# bash NN1.sh 2
1X1=1
1X2=2 2X2=4
[root@LeeMumu ~]# bash NN1.sh 3
1X1=1
1X2=2 2X2=4
1X3=3 2X3=6 3X3=9
[root@LeeMumu ~]# bash NN1.sh 5
1X1=1
1X2=2 2X2=4
1X3=3 2X3=6 3X3=9
1X4=4 2X4=8 3X4=12 4X4=16
1X5=5 2X5=10 3X5=15 4X5=20 5X5=25
[root@LeeMumu ~]# cat NN1.sh
#!/bin/bash
#
#
NN() {
for ((i=1;i<=$1;i++));do
for ((j=1;j<=i;j++));do
echo -e -n "${j}X${i}=$[${i}*${j}]\t"
done
echo
done
}
NN $1
为了便于我们对函数的整体理解,我们在这儿对 本地变量 和 局部变量 进行解释。
本地变量的作用是是运行脚本的shell进程的生命周期;因此,其作用范围为当前shell脚本程序文件。脚本执行时实在shell子进程里执行,脚本执行结束后,变量销毁。
变量赋值:name=value
变量引用:${name}, $name
"":变量名会替换为其值;
'':变量名不会替换为其值;
查看变量:set
撤销变量:unset name
(注意:此处非变量引用;千万不能带 $ 符号,变量引用时才需要带 $ 符号)
局部变量的作用域是函数的生命周期,在函数结束时被自动销毁。
局部变量赋值:local VARIABLE=VALUE
函数变量注意事项:
可参考以下博客,有对脚本变量的详细解释:
https://mp.csdn.net/mdeditor/95232625#bash_234
通过以下示例,大家可以加强对函数变量的理解。
1、函数使用本地变量,会改变本地变量的值。
[root@LeeMumu ~]# bash fUNCTION1.sh # 通过结果,可以得知通过调用函数改变了本地变量
Function: jerry
Shell: jerry
[root@LeeMumu ~]# cat fUNCTION1.sh
#!/bin/bash
#
name=tom # 定义本地变量 name=tom
setname() {
name=jerry # 函数里定义本地变量
echo "Function: $name" # echo 函数变量
}
setname # 调用函数
echo "Shell: $name" # echo 本地变量
2、函数使用本地变量,不会改变本地变量的值。
[root@LeeMumu ~]# bash fUNCTION2.sh # # 通过结果,可以了解局部变量的作用域
Function: jerry
Shell: tom
[root@LeeMumu ~]# cat fUNCTION2.sh
#!/bin/bash
#
name=tom
setname() {
local name=jerry
echo "Function: $name"
}
setname
echo "Shell: $name"
函数递归是指函数直接或间接调用自身。
1、阶乘函数
[root@LeeMumu ~]# bash factTest.sh 1
1
[root@LeeMumu ~]# bash factTest.sh 2
2
[root@LeeMumu ~]# bash factTest.sh 3
6
[root@LeeMumu ~]# bash factTest.sh 11
39916800
[root@LeeMumu ~]# cat factTest.sh
#!/bin/bash
#
#
fact() {
if [ $1 -eq 0 -o $1 -eq 1 ] ;then
echo 1
else
echo $[$1*$(fact $[$1-1])] # () 代表命令引用 [] 代表公式
fi
}
fact $1
2、斐波那契数列
斐波那契数列(Fibonacci sequence),又称黄金分割数列、因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波纳契数列以如下被以递推的方法定义:F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3,n∈N*)在现代物理、准晶体结构、化学等领域,斐波纳契数列都有直接的应用,为此,美国数学会从1963年起出版了以《斐波纳契数列季刊》为名的一份数学杂志,用于专门刊载这方面的研究成果。
[root@LeeMumu ~]# bash fUNCTION3.sh 9
1 1 2 3 5 8 13 21 34
[root@LeeMumu ~]# cat fUNCTION3.sh
#!/bin/bash
#
fab() {
if [ $1 -eq 1 ]; then
echo -n "1 "
elif [ $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、给定一个用户名,取得其用户的 id 号和默认的 shell 。
[root@LeeMumu ~]# bash idShell.sh user33
1006:/bin/bash
[root@LeeMumu ~]# cat idShell.sh
#!/bin/bash
#
userinfo() {
if id "$username" &> /dev/null ; then
grep "^$username\>" /etc/passwd | cut -d: -f3,7
else
echo "No Such User."
fi
}
username=$1
userinfo
2、编写服务框架脚本
#!/bin/bash
#
# chkconfig: - 50 50
# description: test service script
#
prog=$(basename $0)
lockfile=/var/lock/subsys/$prog
start() {
if [ -f $lockfile ]; then
echo "$prog is running yet."
else
touch $lockfile
[ $? -eq 0 ] && echo "start $prog finshed."
fi
}
stop() {
if [ -f $lockfile ]; then
rm -f $lockfile
[ $? -eq 0 ] && echo "stop $prog finished."
else
echo "$prog is not running."
fi
}
status() {
if [ -f $lockfile ]; then
echo "$prog is running"
else
echo "$prog is stopped."
fi
}
usage() {
echo "Usage: $prog {start|stop|restart|status}"
}
case $1 in
start)
start ;;
stop)
stop ;;
restart)
stop
start ;;
status)
status ;;
*)
usage
exit 1 ;;
esac