Shell脚本——函数与数组

169 阅读4分钟

本章重点:

  • 函数
    • 定义函数
    • 调用函数
  • 数组
    • 定义数组
    • 调用数组

一、 函数

在编写脚本时,有些脚本需要反复使用,可以调用函数来解决。函数就是一个功能模块,在函数中写好执行的命令即可。

函数的作用:

  • 使用函数可以避免代码重复,增加可读性,简化脚本;
  • 使用函数可以将一个大的工程分割为若干小的功能模块,代码的可读性更强。

函数使用方法:

  1. 定义函数
  2. 再引用函数

1. 定义函数

定义函数格式:

函数名(){
 命令序列
}
例:
[root@node1 ~]#  func1 (){ hostname;date;}    //定义函数
[root@node1 ~]#  func1                        //调用函数
node1
20240513日 星期一 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. 函数的返回值

  1. 获得函数返回值的两种方式:
  • return表示退出函数并返回一个退出值,脚本中可以用$?变量显示该值。
  • 在函数体中用 echo 输出返回值。并在函数体外使用变量赋值后,可再进一步对函数的返回值进行加工操作。
  1. return使用原则:
  • 函数一结束就取返回值,因为$?变量只返回执行的最后一条命令的退出状态码。
  • 退出状态码必须是0~255,超出时值将为除以256取余。
  1. 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. 函数递归

递归就是一个函数在它的函数体内调用它自身。执行递归函数将反复调用其自身,每调用一次就进入新的一层。所以递归要有两个基本要素,结束条件与递推关系。

  1. 函数调用自己本身的函数
  2. 必须要有结束函数的语句,防止死循环

示例:计算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