本章重点:
- 函数
- 定义函数
- 调用函数
- 数组
- 定义数组
- 调用数组
一、 函数
在编写脚本时,有些脚本需要反复使用,可以调用函数来解决。函数就是一个功能模块,在函数中写好执行的命令即可。
函数的作用:
- 使用函数可以避免代码重复,增加可读性,简化脚本;
- 使用函数可以将一个大的工程分割为若干小的功能模块,代码的可读性更强。
函数使用方法:
- 定义函数
- 再引用函数
1. 定义函数
定义函数格式:
函数名(){
命令序列
}
例:
[root@node1 ~]# func1 (){ hostname;date;} //定义函数
[root@node1 ~]# func1 //调用函数
node1
2024年 05月 13日 星期一 15:12:58 CST
#将常用的功能定义成函数,但是只是临时定义
2. 查看函数
declare -F //查看函数列表
declare -f //查看函数具体的定义内容
declare -F 函数名 //查看指定函数列表
declare -f 函数名 //查看指定函数具体的定义内容
3. 删除函数
unset 函数名 //删除函数
4. 函数文件
我们可以事先创建一个函数库文件,在里面定义各种常用的函数,然后可以在别的shell脚本中直接引用这个函数库文件,使得不需要再次定义函数即可直接调用函数。
注意:"source"和 "." 是在当前shell环境中运行脚本。
如果函数库文件中定义了变量的话,切换bash环境就不生效了。所以在脚本中引用函数库文件时,一定要使用"source"或 "." 。
引用函数库文件时,建议使用绝对路径。避免找不到该文件。
5. 函数作用范围
函数在shell脚本中仅在当前的shell环境中有效
shell脚本中函数的变量默认全局有效
将变量限定在函数内部使用local命令
[root@node1 ~]# name=wang
[root@node1 ~]# func1 () { name=zhou; }
[root@node1 ~]# func1
[root@node1 ~]# echo $name
zhou
[root@node1 ~]# func1 () { local name=zhou; } //加上local变量即可将变量限制在函数内
[root@node1 ~]# name=wang
[root@node1 ~]# func1
[root@node1 ~]# echo $name
wang
注意:先定义再调用!
6. 函数的返回值
- 获得函数返回值的两种方式:
return表示退出函数并返回一个退出值,脚本中可以用$?变量显示该值。- 在函数体中用
echo输出返回值。并在函数体外使用变量赋值后,可再进一步对函数的返回值进行加工操作。
- return使用原则:
- 函数一结束就取返回值,因为$?变量只返回执行的最后一条命令的退出状态码。
- 退出状态码必须是0~255,超出时值将为除以256取余。
- echo
- 因为return的返回值的范围是0-255,超过部分除以256取余,得不到我们想要的结果,此时可以直接在函数体中使用echo命令。
7. 函数传参
[root@node1 ~]# cat cc.sh //脚本内容
sum1() {
echo $1
echo $2
}
read -p "输入第一个参数:" first
read -p "输入第二个参数:" second
sum1 $second $first
[root@node1 ~]# bash cc.sh //执行脚本
输入第一个参数:a
输入第二个参数:b
b
a
8. 函数递归
递归就是一个函数在它的函数体内调用它自身。执行递归函数将反复调用其自身,每调用一次就进入新的一层。所以递归要有两个基本要素,结束条件与递推关系。
- 函数调用自己本身的函数
- 必须要有结束函数的语句,防止死循环
示例:计算5的阶乘
0=1
1=1
1*2=2
1*2*3=6
1*2*3*4=24
1*2*3*4*5=120
1.for循环编写:
[root@node1 ~]# vim jc.sh //编写脚本
#!/bin/bash
sum=1
i=1
read -p "请输入阶乘的值:" num
for i in `seq $num`
do
sum=$[i*sum]
done
echo $sum
[root@node1 ~]# bash jc.sh //执行脚本
请输入阶乘的值:5
120
2.函数编写:
[root@node1 ~]# vim hsjc.sh //编写脚本
#!/bin/bash
fact() {
if [ $1 -eq 1 ];then
echo 1
else
t=$[$1-1] //求前一个数的大小
r=$(fact $t) //前一个数的阶乘结果
echo $[$1 * r] //$1的阶乘结果等于前一个数的阶乘结果乘以$1
fi
}
read -p "请输入:" n
fact $n
[root@node1 ~]# bash hsjc.sh //执行脚本
请输入:5
120
二、 数组
数组(Array)是有序的元素序列。若将有限个类型相同的变量的集合命名,那么这个名称为数组名。
- 普通数组:下标是数值 (默认普通属组,可以不声明)
- 关联数组:下标是有含义的字符 (定义关联数组,一定要先申明!不申明没有效果)
数组名和索引:
- 索引的编号从0开始,属于数值索引
- 索引可支持使用自定义的格式,而不仅是数值格式,即为关联索引,bash 4.0版本之后开始支持
- bash的数组支持稀疏格式(索引不连续)
1. 声明数组
普通数组可以不事先声明,直接使用
declare -a ARRAY_NAME
关联数组必须先声明,再使用
declare -A ARRAY_NAME
2. 定义数组
Shell中,数组用括号来表示,元素用"空格"符号分隔开。
定义格式:
法1(一次赋值全部元素):
`数组名=(元素1 元素2 ......)`
例:a=(10 20 30 40 50)
法2(只赋值特定元素):
`数组名=([0]=value [1]=value [2]=value ...)`
例:a=([0]=10 [1]=20 [2]=30)
法3(通过下标值,一个一个定义数组内的每个元素):
同时也可以通过这种方式修改元素的值
`数组名[下标]=数组值`
数组名[0]="value"
数组名[1]="value"
数组名[2]="value"
法4(先定义列表,再引用列表的值定义数组):
列表名="value0 value1 value2 ..."
数组名=($列表名)
例:
[root@localhost data]#b="1 2 3 4 5 6"
[root@localhost data]#c=$b
[root@localhost data]#echo ${c[*]}
1 2 3 4 5 6
3. 追加数组的值
数组名[下标]=数组值
例子:
a[5]=60
4. 定义关联数组
必须先申明!否则没有效果
` declare -A 数组名`
例子:
[root@node1 ~]# declare -A family
[root@node1 ~]# family[name]=smith
[root@node1 ~]# family[address]=houston
[root@node1 ~]# family[people]=5
[root@node1 ~]# family[car]=3
[root@node1 ~]# echo ${family[address]}
houston
[root@node1 ~]# echo ${family[car]}
3
5. 调用数组中的值
a = (10 20 30 40)
`调用全部`
echo ${a[@]} //a是数组名
echo ${a[*]}
`调用某一个元素 `
echo ${a[下标]} //下标从0开始,不一定是连续的
echo ${a[0]} //调用数组a中第一个元素
6. 显示数组元素个数
echo ${#a[@]}
echo ${#a[*]}
7. 显示所有下标
echo ${!a[@]}
echo ${!a[*]}
8. 数组遍历
[root@node1 ~]# vim szbl.sh
#!/bin/bash
a=(20 40 50 70)
for i in ${a[@]}
do
echo $i
done
[root@node1 ~]# bash szbl.sh //执行脚本
20
40
50
70
9. 数组切片
${数组名[@]:起始位置:截取长度} //起始位置从0算起
${数组名[@]} //获取整个数组的元素值
${数组名[@]:0:3} //从下标为0的元素开始截取,共截取3个元素。(即截取元素1到元素3)
${数组名[@]:2:2} //从下标为2的元素开始截取,共截取2个元素。(即截取元素3到元素4)
10. 删除数组
unset 数组名 //删除整个数组
unset 数组名[下标] //删除数组内的某一个元素
例子:
[root@node1 ~]# a=(1 2 3 4 5)
[root@node1 ~]# unset a[2] //删除元素2
[root@node1 ~]# echo ${a[*]}
1 2 4 5