Shell 函数的本质是一段可以重复使用的脚本代码,这段代码被提前编写好了,放在了指定的位置,使用时直接调取即可。使用Shell函数可以将大的工程分割为若干小的功能模块,代码的可读性更强。
函数定义的格式有两种,一种是使用function
定义的,另一种是函数名定义的。具体定义方式如下:
方式一
function name{
命令序列
}
方式二
name(){
命令序列
}
其中对各个部分的说明:
function
是 Shell 中的关键字,专门用来定义函数;name
是函数名;{ }
包围的部分称为函数体,调用一个函数,实际上就是执行函数体中的代码。 函数返回值的方式有两种,分别是return
和echo
。
使用return返回值:
使用return返回值,只能返回0-255的整数,超出的值将会除以256取余。
函数使用return返回值,通常以用$?
变量显示该值,因此通常仅返回0或1;0表示成功,1表示失败。
例如:
function db1 {
read -p "请输入:" value
return $[$value *2]
}
db1
echo $?
使用echo返回值:
使用echo可以返回任何字符串结果。
通常用于返回数据,比如一个字符串值或者列表值。好处就是可以再次利用echo来对输出的值做二次操作
例如:
db1(){
read -p "请输入" value
echo $[$value * 2]
}
result=`db1`
echo $result
函数体内部
$1
$2
代表的是调用函数时,函数后面跟的位置参数$#
代表调用函数时,函数后面跟的参数个数$@
$*
代表调用函数时,函数后面跟的所有参数函数体外部
$1
$2
代表的是执行脚本时,脚本后面跟的位置参数$#
代表脚本后面跟的参数个数$@
$*
代表脚本后面跟的所有参数其中不管在函数体内还是在函数体外,$0
都代表脚本本身。
例子1:基本用法
[root@localhost ~]# vim demo1.sh
#!/bin/bash
sum2(){
#函数体内部的$1 $2代表的是调用函数时,函数后面跟的位置参数
sum=$[$1 - $2]
echo $sum
}
#函数体外的$1 $2代表的是执行脚本时,脚本后面跟的位置参数
sum2 $2 $1
[root@localhost ~]# bash demo1.sh 10 20
30
例子2:其他用法
[root@localhost ~]# vim demo2.sh
#!/bin/bash
#定义函数
sum2(){
#函数体内部的$1 $2代表的是调用函数时,函数后面跟的位置参数
sum=$[$1 - $2]
echo $sum
echo "在函数体内部的\$#代表调用函数时,函数后面跟的参数个数,当前函数后面有$#个参数"
echo "在函数体内部的\$Q@代表调用函数时,函数后面跟的所有参数,当前函数后面的参数有:$@"
echo "在函数体内部,\$0代表$0"
}
#调用函数
echo "在函数体外时,\$#代表脚本后面跟的参数个数,当前脚本后面有$#个参数"
echo "在函数体外时,\$@代表脚本后面跟的所有参数,当前脚本后面参数有:$@"
echo "在函数体外,\$0代表$0"
#函数体外的$1 $2代表的是执行脚本时,脚本后面跟的位置参数
sum2 $2 $1
[root@localhost ~]# bash demo2.sh 10 20
在函数体外时,$# 代表脚本后面跟的参数个数,当前脚本后面有 2 个参数
在函数体外时,$@代表脚本后面跟的所有参数,当前脚本后面参数有:10 20
在函数体外,$0代表demo2.sh
demo2.sh:行4: cho: 未找到命令
在函数体内部的$#代表调用函数时,函数后面跟的参数个数,当前函数后面有2个参数
在函数体内部的$Q@代表调用函数时,函数后面跟的所有参数,当前函数后面的参数有:20 10
在函数体内部,$0代表demo2.sh
source
或者.
执行脚本,也会影响系统的当前Shell环境)例子1:外面定义的i被函数里面的定义i所覆盖,所以输出函数里面的数字
[root@localhost ~]# vim demo3.sh
#!/bin/bash
list(){
i=7
echo $i
}
i=4
list
echo $i
[root@localhost ~]# bash demo3.sh
7
7
例子2:函数里面的i被定义成了局部变量在函数外不生效
[root@localhost ~]# vim demo4.sh
#!/bin/bash
list(){
local i=9
echo $i
}
i=8
list
echo $i
[root@localhost ~]# bash demo4.sh
9
8
递归就是一个函数在它的函数体内调用它自身。执行递归函数将反复调用其自身,每调用一次就进入新的一层。所以递归要有两个基本要素,结束条件与递推关系。
递归的两个基本要素:
1.边界条件:确定递归到何时结束,也被称为递归的出口
2.递归所使用的模式:大问题分解成小问题。递归函数只有具有上述两个条件,才能计算出结果。
例子1:使用递归计算阶乘
[root@localhost ~]# bash demo5.sh
#!/bin/bash
fact() {
#$1表示调用函数时,后面跟的位置参数。如果值是1,就输出1。
if [ $1 -eq 1 ]
then
echo 1
else
#定义一个本地变量temp,变量值为每次传入的参数值减1
local temp=$[$1 - 1]
#不停地调用函数自身,直到temp的值等于1
local result=$(fact $temp)
echo $[$1 * $result]
fi
}
########## main ###########
read -p "请输入一个正整数:" num
a=$(fact $num)
echo "$num 的阶乘为:$a"
[root@localhost ~]# bash demo5.sh
请输入一个正整数:5
5 的阶乘为:120
例子2:使用递归查询目录
[root@localhost ~]# vim demo6.sh
function list_files {
for f in `ls $1`
do
#判断opt目录下的各个子目录以及文件
if [ -d "$1/$f" ]
then
#opt目录下的目录首行缩进,如果是查询第一次就不缩进,如果不是第一次查询就调用之前的$2的进行首行缩进
echo "$2/$f"
#递归查询opt目录下子目录下的文件和目录,并且将$2空两个字符进行缩进
list_files "$1/$f" " $2"
else
#如果是文件直接输出
echo "$2$f"
fi
done
}
list_files "/opt" ""
[root@localhost ~]# bash demo6.sh
/data
/aa
/bb
/cc
/dd
abcd.txt
/rh
我们可以事先创建一个函数库文件,在里面定义各种常用的函数,然后可以在别的shell脚本中直接引用这个函数库文件,使得不需要在新的shell脚本中再次定义函数而是可以直接调用此函数。
注意:
1.
source
和.
是在当前shell环境中运行脚本。2.如果函数库文件中定义了变量的话,切换bash环境就不生效了。所以在脚本中引用函数库文件的时候,一定要使用
source
或者.
。3.引用函数库文件时候 ,建议使用绝对路径,避免文件丢失。
创建一个函数库文件
[root@localhost ~]# vim funcs.sh
#!/bin/bash
jiafa() {
echo $[$1 + $2]
}
jianfa() {
echo $[$1 -$2]
}
chengfa() {
echo $[$1 * $2]
}
chufa() {
if [ $2 -eq 0 ];then
echo "除数不可为0"
else
echo $[$1 / $2]
fi
}
fact() {
if [ $1 -eq 1 ];then
echo 1
elif [ $1 -gt 1 ];then
local tmp=$[$1 - 1]
local res=$(fact $tmp)
echo $[$1 * res]
else
echo "输入的值无效。请输入大于等于1的整数!"
fi
}
在其他的shell脚本中执行函数
[root@localhost ~]# vim demo7.sh
#!/bin/bash
#加载函数库文件到当前脚本的shell
. /root/funcs.sh
read -p "请输入第一个正整数:" value1
read -p "请输入第二个正整数:" value2
res1=$(jiafa $value1 $value2)
res2=$(jianfa $value1 $value2)
res3=$(chengfa $value1 $value2)
res4=$(chufa $value1 $value2)
res5=$(fact $value1 $value2)
echo "加法的结果为$res1"
echo "减法的结果为$res2"
echo "乘法的结果为$res3"
echo "除法的结果为$res4"
echo "阶乘的结果为$res5"
[root@localhost ~]# bash demo7.sh
请输入第一个正整数:10
请输入第二个正整数:5
加法的结果为15
减法的结果为5
乘法的结果为50
除法的结果为2
阶乘的结果为3628800