shell编程之函数以及函数中的递归
目录
前言
一、什么是函数
二、获取函数的返回值
示例1:return
示例2:echo
三、函数传参
四、函数变量的作用范围
五、递归
示例1:求阶乘
示例2:递归目录
六、函数库
七、总结
前言
初学者写的脚本
#/bin/bash#准备工作mount /dev/cdrom /mntcd /etc/yum.repos.dmkdir repo.bakmv *.repo repo.bak#创建本地yum源仓库配置文件echo '[local]' >> local.repoecho 'name=local' >> local.repoecho 'baseurl=file:///mnt' >> local.repoecho 'enabled=1' >> local.repoecho 'gpgcheck=0' >> local.repoyum clean all && yum makecache#安装httpd服务,并启动,设置开机自启动yum install -y httpdsystemctl start httpdsystemctl enable httpd
这个脚本乍一看,没有什么问题,能够正常执行,但是流水账似的脚本,公司同事一看你就是个新手
老员工看一个人的脚本写的多不多,规不规范:
1.看你写的格式
2.有没有大量使用函数
老手修改后的脚本:
#/bin/bash#准备工作read_work () {mount /dev/cdrom /mntcd /etc/yum.repos.dmkdir repo.bakmv *.repo repo.bak }#创建本地yum源仓库配置文件create_repo () {echo '[local]' >> local.repoecho 'name=local' >> local.repoecho 'baseurl=file:///mnt' >> local.repoecho 'enabled=1' >> local.repoecho 'gpgcheck=0' >> local.repoyum clean all && yum makecache }#安装httpd服务,并启动,设置开机自启动install_soft () {yum install -y httpdsystemctl start httpdsystemctl enable httpd}## main #read_workcreate_repoinstall_soft
这就是学函数的意义,使得外人读起来很方便理解你的代码逻辑
一、什么是函数
- 使用函数可以避免代码重复
- 使用函数可以将大的工程分割为若干小的功能模块,代码的可读性更强
- 类似于Java的方法
#函数定义1function 函数名 { }#函数定义2函数名() { }## main ##可以在主代码区域中直接使用函数名调用函数函数名
法一:function 函数名 { 三部分之间都要有空格
法二:函数名() { 函数名和括号之间空格可有可无
二、获取函数的返回值
return表示退出函数并返回一个退出值,脚本中可以用$?变量结合echo输出该值
在函数体中用echo输出,并在函数体外可以使用变量赋值后可进一步对返回值操作
使用原则:
1.函数一结束就取返回值,因为$?变量只返回执行的最后一条命令的退出状态码
2.退出状态码必须是0~255,超出时值将除以256取余
$?作用一:显示上一条命令是否正确
$?作用二:显示退出函数并返回的退出值
示例1:return
#!/bin/bash#在文件上方先定义函数,然后在主代码部分调用函数function test1 {#function test1 { 之间都要有空格,否则执行不成功 read -p "请输入一个整数:" num return $num}# main 可以在主代码区域中直接使用函数名调用函数test1#调用函数echo $?#输出返回值
示例2:echo
因为return的返回值的范围是0-255,超过部分除以256取余,得不到我们想要的结果,所以我们使用echo
#!/bin/bash#在文件上方先定义函数,然后在主代码部分调用函数function test1 {#function test1 { 之间都要有空格,否则执行不成功 read -p "请输入一个整数:" num return $[num * 2]}# main 可以在主代码区域中直接使用函数名调用函数test1echo $?#输出返回值
#!/bin/bash#在文件上方先定义函数,然后在主代码部分调用函数function test1 {#function test1 { 之间都要有空格,否则执行不成功 read -p "请输入一个整数:" num echo $[num * 2]#改为echo直接在函数中输出}# main 可以在主代码区域中直接使用函数名调用函数test1
这个要自己多操作,光看写的东西不能够很好的领悟到其中的含义
三、函数传参
sum1() { sum=$[$1 + $2] echo $sum}# main ##read -p "请输入第一个参数:" firstread -p "请输入第二个参数:" secondsum1 $first $second
四、函数变量的作用范围
- 函数在shell脚本中仅在当前shell脚本环境中有效(用source执行脚本,在当前系统shell环境运行)
- shell脚本中变量默认全局有效
- 将变量限定在函数内部使用local命令
#!/bin/bashi=8i=9echo $i#输出9,因为i的值被覆盖了
#!/bin/bashmyfun() { i=9}# main ##myfuni=8echo $i#输出8#主函数开始运行,第一步函数调用i=9,然后i=8覆盖后输出
#!/bin/bashmyfun() { i=9 echo $i}# main ##myfuni=8echo $i#输出98#调用函数时输出函数里面i=9,而后输出的i被i=8覆盖
#!/bin/bashmyfun() { local i #local定义的变量只能在local之后到函数结束前有效 i=9 echo $i}# main ##i=8myfunecho $i#输出98,而不是99#local限定了函数i的值,函数外不能重新覆盖,而没有local的变量时全局有效的
五、递归
简单来讲递归就是不停地调用本身,比较有名的递归算法有斐波拉契数列、汉诺塔等,有兴趣的小伙伴可以自行查阅资料了解一下
示例1:求阶乘
#!/bin/bash#使用递归计算阶乘fact() { if [ $1 -eq 1 ] #$1,表示函数后面调用的变量,如果调用的值是1,输出值就是1 then echo 1 else local temp=$[$1-1] #定义一个变量temp为传入函数的参数减1,并且为局部变量 local result=$(fact $temp) #每次调用函数自己,每次temp减1,直到为1 echo $[$1 * $result] #用c语言理解就是这样一个过程: num*fact(num-1) fi}## main ##read -p "请输入一个正整数:" numresult=$(fact $num)#跟上面的result不影响,上面的result是局部变量,仅在函数内有效echo "$num 的阶乘为: $result"
实现过程
fact 5
- $1=5 temp=4 result=$(fact 4) echo 5 * $(fact 4)
- $1=4 temp=3 result=$(fact 3) echo 5 * 4*$(fact 3)
- $1=3 temp=2 result=$(fact 2) echo 5 * 4 * 3*$(fact 2)
- $1=2 temp=1 result=$(fact 1) echo 5 * 4 * 3 * 2*$(fact 1)
- $1=1 2*1
示例2:递归目录
mkdir -p /root/bin/aa/bb/cc/dd ; touch /root/bin/aa/bb/cc/dd/abc.txt
递归/root/bin目录,显示他的所有子目录和文件
#!/bin/bash#递归/root/bin目录,显示他的所有子目录和文件list() { for fd in $1/*#逐个检查$1参数指定目录下的所有文件或子目录 do if [ -d $fd ]#判断如果是目录就输出,并且通过递归把这个目录下的所有文件或子目录再逐个检查,如果发现还有子目录会按照这个方式一直检查下去 then echo "$fd 是目录" list "$fd" #递归函数调用 else echo "$fd 是文件" fi done}## main ##list "/root/bin"
六、函数库
可以事先创建一个函数库,在里面定义各种常用的函数然后可以在别的shell脚本中直接引用
#函数库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 else local temp=$[$1-1] local result=$(fact $temp) echo $[$1 * $result] fi}
一定要在脚本中加载函数库,用source或者点(.)
七、总结
定义函数法一:
function 函数名 {
命令序列
}
定义函数法二:
函数名(){
命令序列
}
注意:
-
同名函数 后一个生效
-
调用函数一定要先定义