bash脚本编程--函数
在bash中,函数是由命令和语句结构构成的能够实现特定功能的集合;
为什么要在bash中引入函数?
在bash脚本编写过程中有可能会出现重复且不做任何改变的代码内容,如果这类内容全靠原始代码书写的话不易于排错和优化;因此我们可以选择将此类代码封装在函数中,在适当的场景中可以重复调用执行;
像此类被封装起来的代码块通常称其为模块,也叫函数;
注意:
1.想要使用函数,必须在使用前先定义出来;
2.如果在某个bash脚本中包含了函数体,默认函数体中的各命令和语句不会执行的;只有在调用函数名的时候,才会执行函数体中的命令和语句;
3.通常需要重复执行的代码块或命令集,可以封装成函数;
4.被调用的函数只能在调用的函数的shell中被执行;
定义函数的方法:
函数有两部分组成:
函数名+函数体
函数名:调用函数时所使用的字符串标识;在一个执行环境中,函数名是不允许重复定义的;
函数体:能够实现特定独立功能的shell命令或结构化语句块;
定义的语法结构:
语法一:
function func_name {
func_body
}
语法二:
func_name() {
func_body
}
注意:在语法二的格式中,func_name和()之间绝对不能有空白字符存在;
注意:函数可以在脚本中被定义,也可以在当前shell中通过交互式环境定义;
函数的使用方法:
函数在定义的时候,其函数体中包含的所有命令或结构化语句都不会被执行;只有在函数被调用时,才能执行其函数体中的各命令和语句;
调用方式:在命令行脚本中,通过直接给出的函数名的方式进行函数调用;
通常可以将常用的函数存放在专门用于保存函数的文件中,如果想要调用这个文件中已经被定义保存的函数,只需要在命令行或脚本中,使用source命令(.命令)加载文件内容到当前shell中,然后在直接使用函数名调用函数即可;
函数的撤销:
unset命令
格式:# unset func_name
注意:可以使用set命令查看当前已经定义并生效的函数;
函数的返回值:
两种返回值:
函数的执行结果返回值:
1.在函数体中所添加的命令有标准输出;
2.在函数体中使用echo或printf命令强制输出返回信息;
函数的执行状态返回值:
1.默认情况下其状态返回值为函数体中最后一条命令的状态返回值;
2.自定义状态返回值或者叫做退出码;
return命令:
return: return [n]
从一个 shell 函数返回
n:0-255(1,2,127为系统保留的状态码,尽量不用)
0:表示无错误返回
1-255:表示有错误返回;
注意:在函数被调用执行时,一旦遇到return命令则不会再继续执行函数体中其他的后续命令,立刻结束此次函数的调用执行;
函数的生命周期:
一般来讲,从函数被调用时开始,知道函数体中所有的命令和结构化语句全部执行完成或遇到return命令,函数的调用结束;
函数的实参:
对应的bash函数来说,没有形参,只有实参;
bash函数的实参是使用$1,$2...位置变量来提供数据的;
func_name pos1 pos2 ...
可以使用$@或者$*表示全部的参数列表;
可以使用$#计算参数的个数;
注意:为函数提供参数时使用的位置变量,是调用函数名时在函数名后面的对用位置上的参数信息;与脚本的位置参数不是一回事;
变量:
函数被调用时,必须在某特定的shell中被调用,因此,函数中可以继承并识别出环境变量和由调用函数shell定义的本地变量;
在函数中还可以定义局部变量;而局部变量仅在函数的生命周期内有效;在结束函数执行之前,应该撤销所有该函数定义的局部变量;
局部变量的定义方法:
local VAR_NAME=VALUE
变量的替换方式:
前提:定义环境变量:
export MYVAR=qhdlink
示例:
#!/bin/bash
#区别全局变量、本地变量、局部变量;
testvar() {
#local命令定义局部变量,只在局部有效;
local MYVAR=chinalink
echo "Internal function: $MYVAR"
}
#全局变量
echo "Global variables: $MYVAR"
MYVAR=link
#本地变量
echo "External function, $MYVAR"
#调用局部变量
testvar
函数的递归调用:
广义:在一个函数中调用另一个函数
狭义:在函数体中调用函数自身;
直接调用:
func1(){
func1
}
间接调用:
func2(){
func1
}
func1(){
func2
}
函数的直接递归调用示例1:
计算某个数字的阶乘:
利用for循环:
#!/bin/bash
#本脚本计算某个数字的阶乘:
#定义fact为1;
fact=1
#如果给定的数字等于0,就输出0的阶乘为$fact为1,如果数字等于1就输出1的阶乘为$fact为1,否则就开始计算输入数字的阶乘;
if [ $1 -eq 0 ] ; then
echo "0! is $fact"
elif [ $1 -eq 1 ] ; then
echo "1! is $fact"
else
#利用for循环计算阶乘,从1开始一直到$1;
for I in $(seq $1) ; do
#阶乘是n!=1×2×3×...×n,所以$1的阶乘是$1!=1×2×3×...×$1;
let fact=$[fact*$I]
done
echo "${1}! is $fact"
fi
#回收变量;
unset fact I
利用函数递归调用:
#!/bin/bash
#本脚本利用函数计算某个数字的阶乘:
#定义函数fact;
fact(){
#如果给定的数字等于0或1就执行显示数字的阶乘等于1,否则就利用函数计算给定数字为非0非1的阶乘;
if [ $1 -eq 0 ] || [ $1 -eq 1 ] ; then
echo 1
else
#用$1减去$($1-1)的阶乘,n的阶乘就是nx(n-1)!的阶乘,n-1的阶乘就是(n-1)x(n-2)!的阶乘,依次类推;
echo "$[$1*$(fact $[$1-1])]"
fi
}
echo "${1}! is $(fact $1)"
函数的直接递归调用示例2:
斐波那波数列(黄金分割数列)
1 1 2 3 5 8 13 21 34 55 ...
#!/bin/bash
#本脚本显示斐波那波数列(黄金分割数列);
#定义函数fabonacci;
fabonacci(){
#如果给定的数字为1或者2就显示数列1,否则就利用函数计算给定数字为非1非2的黄金分割数;
if [ $1 -eq 1 ] || [ $1 -eq 2 ] ; then
echo -n "1 "
else
#函数的式子来自黄金分割数列的规律,利用函数的自我调用来计算显示黄金分割数,给定数字的黄金分割数列规律是:给定数字等于给定数字在数列中的前两个数字之和即如下规律;
echo -n "$[$(fabonacci $[$1-1])+$(fabonacci $[$1-2])] "
fi
}
for I in $(seq $1) ; do
fabonacci $I
done
echo
函数的直接递归调用示例3:
汉诺塔
#!/bin/bash
#本脚本显示给出汉诺塔层数的搬运次数;
#定义步数step为0;
step=0
#定义函数move为挪动盘子的方法:这里简化利用最简单的只有一个盘子的时候,先把盘子挪动到第二个柱子,再把盘子从第二个柱子挪动到第三个盘子;
move(){
#随着盘子的挪动,步数也要进行增长;
let step++
#先把盘子挪动到第二个柱子,再把盘子从第二个柱子挪动到第三个盘子;
echo "$step: move disk $1 $2 --> $3"
}
#定义函数hanoi为移动规律;
hanoi(){
#当塔为1层时,直接先把盘子挪动到第二个柱子,再把盘子从第二个柱子挪动到第三个盘子,否则利用汉诺塔规律和函数的自我调用实现多层数的步数计算;
if [ $1 -eq 1 ] ; then
move $1 $2 $4
else
#除了塔底层最后一个大盘子,将其他的盘子整合到一起由第一个柱子整体挪动到第二个柱子,再将底层的最后一个大盘子挪到第三个柱子,再将第二个柱子上的其他盘子整合挪到第三个柱子:用hanoi挪到两次,再用move挪一次,再使用调用hanoi函数完成最后的把其他盘子整体由第二个柱子移动到第三个柱子;
hanoi "$[$1-1]" $2 $4 $3
move $1 $2 $4
hanoi "$[$1-1]" $3 $2 $4
fi
}
#传递四个参数:层数,A柱,B柱,C柱;
hanoi $1 A B C
#回收变量
unset step A B C