1. 创建函数并简单调用:
#!/bin/bash
#一下两种函数定义完全等价
function f1 {
echo This is func1
}
f2() {
echo This is func2
}
#调用只需要简单地使用函数名就行了
f1
f2
注意!一定要先定义再调用,否则会报错,和C语言不同的是C可以先声明在调用而定义可以放在任何位置,Shell必须把定义放在调用之前,并且没有声明!
2. 重名覆盖:
#!/bin/bash
function f1 {
echo This is func1
}
f1
f1() { #从重复定义的位置开始就把上一个函数的代码完全覆盖了
echo This is func2
}
f1 #输出的是func2
3. 退出函数:
正常退出:是指函数执行完最后一条命令后自然地退出函数,退出码则是最后一条命令的状态码
return命令退出:利用return命令退出,在使用return命令的位置退出,并可以指定一个退出状态码(以return命令的参数的形式体现)
查看函数的推出状态码:使用标准变量$?查看
使用退出状态码的缺陷:
1) 函数已退出就返回
2) 退出状态码的范围限制在0 ~ 255,一旦使用return返回一个超过255的数就会发生错误(模除256的结果)
3) 函数退出后必须第一时间使用$?查看,如果中间隔着其它命令则真正的返回值将会不存在
#!/bin/bash
func() {
echo The first time
ls xxxx #最后一句异常,退出非0
}
func
echo $? #非0
func() {
ls xxxx
echo The second time #最然上一句有异常,但是最后一句正常,退出0
}
func
echo $? #0,因此正常退出通常有漏洞
func() {
read -p "Please input a number: " a
if [ $a -gt 5 ] #可以利用分支情况选择性退出
then
return 1
else
return 0
fi
}
func
echo $?
4. 函数输出:
#!/bin/bash
func() {
read -p "Please input a number: " num
echo $num
}
x=`func` #本来流向STDOUT的文本作为函数的输出返回给变量x,函数输出自动重定向到变量中
echo the func output a number: $x
5. 像使用命令行参数一样向函数传递参数:
由于函数的实质就是一个小脚本,在脚本中调用函数其实就是在该脚本中调用函数的位置再运行函数这个小脚本,因此函数也是一种脚本命令,也具有命令行参数,传参就是使用命令行参数,可以像使用命令行参数的方式在函数中使用传进来的参数。
#!/bin/bash
add() {
if (( 0 == $# || $# > 2 )) #检查参数的个数,0或者大于2个参数都返回-1
then
echo -1
elif (( 1 == $# )) #一个参数则自加一遍返回
then
echo $[ $1 + $1 ]
else #两个参数相加返回
echo $[ $1 + $2 ]
fi
}
while read -p "Please input numbers: " s
ans=`add $s` #将读入的字符串作为传命令行参数传给函数
[ $ans -ne -1 ] #当返回值不为-1时打印结果,否则无限循环下去
do
echo Answer is $ans
done
注意!函数中的$1、$#等是调用函数时穿进去的命令行参数而不是调用它的脚本的命令行参数,要向函数传脚本的命令行参数则必须使用`add $1 $2`的形式
6. 全局变量:
#!/bin/bash
function func {
a=sdflsf #如果函数内部的变量不加任何属性关键字就会把它当做全局变量处理
echo inner first b: $b #定义local之前的b是外部的全局变量
local b=xxxxx #加local关键字后就声明为一个局部变量了,如果函数外有同名变量,则这两个变量时分离的,互不干涉
echo inner second b: $b #从定义local开始后的位置就都是局部变量的作用域了,因此这里访问的是局部变量
}
func
echo outer a: $a #虽然在函数内部使用,但是可以在外部访问,并且其值就是函数内部给它赋的值,因此这里的a和函数内的a是同一个a,没有分离
s=sdlfkjsdf #当然,这显示是在函数外直接定义了一个全局变量,在任何函数内都可以使用
echo outer b: $b #由于和函数内的b是分离的,因此这里的b是空的
b=sdfdsf
func
7. 创建库并使用source命令导入库:
#!/bin/bash
#库文件,里面只有函数定义
#funcs.sh
function add {
echo $[ $1 + $2 ]
}
function mul {
echo $[ $1 * $2 ]
}
function div {
if [ $2 -ne 0 ]
then
echo $[ $1 / $2 ]
else
echo Divide by zero!
fi
}
#!/bin/bash
#.操作就相当于source命令,和source ./funcs.sh,这样就将其他脚本作为模块导入当前脚本中了,可以直接在当前脚本中使用库文件中的函数
. ./funcs.sh
read a b
add $a $b
read a b
mul $a $b
read a b
div $a $b
第二个main.sh导入funcs.sh库使用里面的函数。
8. 在命令行上直接使用函数:
方法一:直接在命令行上定义
#定义在一行的情况
$ function add { echo $[ $1 + $2 ]; } #注意!在命令行上定义函数时每条名ing默认要加;以表示一条命令的结束
$ add 1 3
4
#定义在多行的情况
$ function mul { #以这种方式定义就如同在脚本里直接写一样
> echo $[ $1 * $2 ]
> }
$ mul 1 2
2
方法二:直接在命令行上用source导入函数库
$ . funcs.sh
$ div 5 0
Divide by zero!
注意!以上两种方法中,如果定义的函数和内部命令名相同会覆盖内部命令,但不过在退出终端后这些函数将失效!
方法三:在bash启动文件.bashrc中定义函数,这样就可以当做内部命令使用,并且是永久有效的,.bashrc是bash的配置文件,位于用户主目录下
直接在.bashrc中将函数定义写上去或者使用source命令将函数库导入到.bashrc文件中,这样就可以在命令行上把这些函数当做内部命令来使,因为和内部命令是平起平坐的,因此也可以在任何脚本中像使用ls命令一般直接使用这些函数。
注意!修改后需要重新启动终端才能真正起作用。
9. 递归:
#!/bin/bash
#一个求阶乘的经典例子
function fac {
if [ $1 -eq 1 ]
then
echo 1
else
echo $[ $1 * `fac $[ $1 - 1 ]`] #所有语言的递归函数都是大同小异的
fi
}
while read -p "Please input a number: " n
[ $n -ne 0 ]
do
echo `fac $n`
done
10. 可变数组:
#!/bin/bash
arr=(one two three four five) #使用圆括号和空格分隔符来定义数组(学名叫可变数组)
echo $arr #显示第一个元素
echo ${arr[2]} #显示2号元素,索引从0开始,宏替换的时候需要下标,因此为${arr[index]}
#因为arr[2]这整个整体作为一个完整的宏,如果直接$arr[2]则会输出one[2]
echo ${arr[*]} #显示所有元素
for x in ${arr[*]} #逐个解析数组元素
do
echo $x
done
arr[2]=kkkk
echo ${arr[2]}
echo ${arr[*]}
unset arr[2] #删除相应下标的元素
echo ${arr[*]} #看上去好像是真的删除了
echo ${arr[2]} #实际上是将arr[2]置成NULL了,并没有将这个位置也删除掉
#因此命令unset只能将数组中某个位置的元素设成NULL
echo ${arr[10000]} #正向越界返回NULL
echo ${arr[-1]} #反向越界直接报错
11. 和函数之间传递可变数组的问题:
将数组传入函数并在函数中当做数组使用:
#!/bin/bash
function prc {
#由于传进去的是一个一个参数,这和普通命令行传参没两样,因此要在函数中使用数组则必须将一个个参数重构成一个新的数组
local arr=(`echo "$@"`) #先获得参数列表(参数使用空格分开的),然后再用()运算符构造一个新的数组
echo The new arrary is ${arr[*]} #这样就可以通过下标访问了
echo arr[2] is ${arr[2]}
echo -n "and sum is "
local sum=0
for (( i = 0; i < $#; i++ ))
do
sum=$[ $sum + ${arr[$i]}]
done
echo $sum
echo -n "also can be "
sum=0
for x in "$@"
do
sum=$[ $sum + $x ]
done
echo $sum
}
arr=(1 2 3 4 5 6)
prc ${arr[*]} #传进去的时候只能将数组元素分解成一个个参数穿进去,而不能直接穿一个$arr,这表示只传第一个元素进去
从函数中返回数组: