shell 编程之函数

简介

把一段独立功能的代码当作一个整体,并为之一个名字;命名的代码段,此即为函数。函数存在的意义,就是为了可以更加方便地进行代码重用。

函数不会自动执行,只有在代码中调用函数名称,才会在调用位置执行函数所包含的代码段。

shell 编程的函数有两种语法:

1
2
3
4
5
6
7
8
9
# 语法一:
function f_name {
...函数体...
}

# 语法二:
f_name() {
...函数体...
}

返回值与返回状态

函数的返回值,即函数内代码段执行的输出结果,比如:

1
2
3
4
test_fuc() {
echo 123
}
test_fuc

则此函数的返回值,就是 123

函数的返回状态,即函数体中运行的最后一条命令的状态结果,也可以使用 return 进行自定义返回状态。

1
2
3
4
5
6
7
test_fuc() {
id hzz
return_id=$?
return $return_id
}
test_fuc
echo $?

执行到 return 后,函数会中断并退出,且返回 return 后面所跟的状态值。

接收参数

函数可以使用脚本中定义的变量,也可以接收脚本提供的参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 直接使用脚本变量
iname=hzz
id_name() {
id $iname
}
id_name

# 接收脚本参数
iname=hzz
id_name() {
id $1
}
id_name $iname

注意:函数体内所获取的 $1 ,不是脚本的 $1 参数,而是脚本传递给函数体的 $1 参数,其他传参类似。其实可以将整个函数体看做是一个完整的脚本,引用函数可看做是调用并执行脚本文件。

局部变量

在 shell 脚本中,直接定义的变量只在脚本中生效。同样的,在函数中,也可以并且推荐使用 local 定义局部变量,使得变量只在函数中生效。

函数内部尽量使用局部变量,以免跟主程序冲突。除非此函数的备份功能是为了改变主程序的变量。

1
2
3
4
5
6
7
iname=hzz
id_name() {
local iname=xyz
echo $iname
}
id_name
echo $iname

从执行结果可以看到,iname 并没有被函数改变,因为函数定义的是局部变量。

函数递归

函数递归,即函数直接或间接调用其本身。要小心使用,避免造成死循环。

1
2
3
4
5
6
7
8
9
10
11
12
test_fuc() {
iname=$1
if [[ "$iname" == "hzz" ]]; then
echo right
iname=xyz
test_fuc $iname
else
echo wrong
return 1
fi
}
test_fuc hzz

示例

服务脚本框架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#!/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

根据参数创建10个用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/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}
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

计算 N! 运算

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
#
fact() {
if [ $1 -eq 0 -o $1 -eq 1 ]; then
echo 1
else
echo $[$1*$(fact $[$1-1])]
fi
}

fact $1

序列

1,1,2,3,5,8... 后一个数是前面的数字之和。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/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

打印 NN 乘法表

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
#
max=$1
m_table() {
local max=$max
for ((i=1;i<=$max;i++)); do
for ((j=1;j<=$i;j++)); do
echo -n "$j*$i=$[$i*$j] "
done
echo
done
}
m_table