函数与数组
函数
Shell 函数
- 将命令序列按格式写在一起
- 可方便重复使用命令序列
- 使用函数可以避免代码重复
- 使用函数可以将大的工程分割为若干小的功能模块,代码的可读性更强
Shell 函数定义
方式一:
function 函数名 {
命令序列
}
方式二:
函数名 () {
命令序列
}
函数返回值
- return表示退出函数并返回一个退出值,脚本中可以用 $? 变量显示该值
使用原则
- 函数一结束就取回返回值,因为 $? 变量只返回执行的最后一条命令的退出状态码
- 退出状态码必须是0-255,超出时值将为除以256取余 exit 0
举例:
方式一:
[root@gcc jiaoben1]#vim test3.sh
#!/bin/bash
function abc { #使用function进行函数定义
read -p "请输入:" a
a=$[$a*2]
return $a #return表示退出函数并返回一个退出值,脚本中可以用 $? 变量显示该值,我们执行的返回码用于判断命令是否执行成功
}
abc
echo $? #退出状态码必须是0-255,超出时值将为除以256取余
function abc {
read -p "请输入:" a
a=$((a*2))
return 0
}
# 调用函数并将其返回值保存在result变量中
abc
result=$?
# 检查返回值是否为0,并打印result变量的值
if [ $result -eq 0 ]
then
echo "函数返回值为:$a"
else
echo "函数执行出错!"
fi
补充:
return和exit的区别在于它们的作用范围:return只作用于函数内部,而exit作用于整个脚本。
如果想从函数中返回一个值并继续执行脚本,应该使用return语句;
如果想在任何时候结束整个脚本的执行,应该使用exit语句。
在这个例子中,abc 函数会提示用户输入一个数,并将输入的数乘以 2 作为返回值。
当 abc 函数执行完毕后,使用 echo $? 命令输出函数的退出状态码。
由于 abc 函数返回的是一个数值,因此它的退出状态码也是一个数值。
在这个例子中,假设用户输入的是 3,
那么 abc 函数将会返回 6。因此,当执行 echo $? 命令时,输出的结果将会是 6。
需要注意的是,函数的返回值只能是一个整数值,
而且在 Shell 中,退出状态码必须在 0 到 255 之间。
如果计算结果超出了这个范围,需要对结果进行取余操作,以确保它是一个合法的退出状态码。
在 Shell 脚本中,return 语句用于指定函数的退出状态码(返回码)。约定俗成地,
返回码为 0 表示成功,非零值表示失败或错误。
返回码的具体含义是由你自己定义的,只要符合约定即可。你可以根据自己的需要定义不同的返回码和其对应的含义。
Shell中的 return 命令通常用于在函数内部控制流程,并将状态码或结果传递给调用者。
它的主要作用是告诉调用者函数的执行状态或结果,并不像其他语言中的 return 语句那样直接返回函数结果。
Shell中的 return 命令主要用于控制函数的退出状态码或返回结果,以便在函数调用后进行相应的处理。
虽然Shell中的 return 不同于其他语言中的返回语句,但在Shell脚本编程中,它仍然具有一定的作用和用途。
在编写 Shell 脚本时,根据函数的执行结果来设置适当的返回码是一种良好的编程习惯,
它可以帮助调用者根据返回码来判断函数的执行状态,并根据需要采取相应的措施
在 Shell 函数中,可以使用 exit 语句来终止函数的执行并返回一个退出状态码。
当函数执行到 exit 语句时,整个脚本将立即停止执行,并将指定的退出状态码返回给调用者
方法二:
[root@gcc jiaoben1]#vim test4.sh
#!/bin/bash
abc () {
read -p "请输入:" a
a=$[$a*2]
echo $a
}
abc #直接使用函数名进行运算,这里加了abc就会输出两次
或者
result=$(abc)
echo $result
----------------------------------------------------------------
[root@gcc jiaoben1]#./test4.sh
请输入:400
800
函数传参
- 在Shell中,调用函数时可以向其传递参数。
- 在函数体内部,通过 $n 的形式来获取参数的值,
- 例如,$1表示第一个参数,$2表示第二个参数…即使用位置参数来实现参数传递。
方式一:
[root@gcc jiaoben1]#vim test5.sh
#!/bin/bash
sum1 () {
sum=$[$1 + $2]
echo $sum
}
read -p "请输入第一个参数:" first
read -p "请输入第二个参数:" second
sum1 $first $second:
----------------------------------------------------------------------------
[root@gcc jiaoben1]#chmod +x test5.sh
[root@gcc jiaoben1]#./test5.sh
请输入第一个参数:2
请输入第二个参数:3
5
方式二:
[root@gcc jiaoben1]#vim test5.sh
#!/bin/bash
sum1 () {
sum=$[$1 + $2]
echo $sum
}
sum1 $1 $2
---------------------------------------------------------------------------
[root@gcc jiaoben1]#./test5.sh 10 20
30
函数变量的作用范围
- 函数在shell脚本中仅在当前shell环境中有效
- shell脚本中的变量默认全局有效
- 将变量限定在函数内部使用local命令
示例
[root@gcc jiaoben1]#vim test5.sh
#!/bin/bash
abc () {
a=5
b=6
return $a $b #在函数内部定义了全局变量,外部的赋值不能改变全局变量
}
a=8
b=12
c=9
abc
echo "a等于$a"
echo "b等于$b"
echo "c等于$c"
#!/bin/bash
abc () {
local a=5 #用了local就是函数内部的变量,外部重新赋值会替换这个变量
local b=6
return $a $b
}
a=8
b=12
c=9
abc
echo "a等于$a"
echo "b等于$b"
echo "c等于$c"
[root@gcc jiaoben1]#vim test5.sh
#!/bin/bash
abc () {
local i=8
echo "inside $i"
}
i=9
abc
echo "outside $i"
------------------------------------------------------------------------
[root@gcc jiaoben1]#./test5.sh
inside 8
outside 9
[root@gcc jiaoben1]#vim test5.sh
#!/bin/bash
abc () {
echo "inside1 $i"
let i++
local i
i=8
echo "inside2: $i"
}
i=9
abc
echo "outside $i"
----------------------------------------------------------------
[root@gcc jiaoben1]#./test5.sh
inside1 9
inside2: 8
outside 10
函数递归
- 函数调用自己本身的函数
阶乘
阶乘,也是数学里的一种术语。阶乘指从1乘以2乘以3乘以4一直乘到所要求的数。 6 12345*6 8 12345678例如所要求的数是4,则阶乘式是1×2×3×4,得到的积是24,24就是4的阶乘。 例如所要求的数是6,
则阶乘式是1×2×3×……×6,得到的积是720,720就是6的阶乘。例如所要求的数是n,则阶乘式是1×2×3×……×n,设得到的积是x,x就是n的阶乘。
在表达阶乘时,就使用“!”来表示。如h阶乘,就表示为h!
[root@gcc jiaoben1]#vim test6.sh
#!/bin/bash
fact () {
if [ $1 -eq 1 ]
then
echo 1
else
local temp=$[$1 - 1]
local result=$(fact $temp)
echo "$[$1*$result]"
fi
}
read -p "请输入阶乘数:" n
result=`fact $n`
echo "$result"
阶乘解释
调用fact函数,并将5作为输入参数传递进去。
在fact函数内部,判断输入参数是否等于1。由于输入参数为5,所以不等于1。
将输入参数减1,得到4。然后递归调用fact函数,并将4作为输入参数传递进去。
在新的fact函数内部,同样判断输入参数是否等于1。由于输入参数为4,所以不等于1。
将输入参数减1,得到3。然后递归调用fact函数,并将3作为输入参数传递进去。
在新的fact函数内部,同样判断输入参数是否等于1。由于输入参数为3,所以不等于1。
将输入参数减1,得到2。然后递归调用fact函数,并将2作为输入参数传递进去。
在新的fact函数内部,同样判断输入参数是否等于1。由于输入参数为2,所以不等于1。
将输入参数减1,得到1。然后递归调用fact函数,并将1作为输入参数传递进去。
在新的fact函数内部,判断输入参数是否等于1。由于输入参数为1,所以等于1。
直接返回1。
回到第9步的fact函数中,将输入参数1乘以结果1,得到1。
回到第7步的fact函数中,将输入参数3乘以结果1,得到3。
回到第5步的fact函数中,将输入参数4乘以结果3,得到12。
回到第3步的fact函数中,将输入参数5乘以结果12,得到60。
将60作为返回值返回给主程序,存储在result变量中。
打印result变量的值,即60。
递归目录
[root@gcc jiaoben1]#vim test7.sh
#!/bin/bash
function list_files {
for f in `ls $1`
do
if [ -d "$1/$f" ]
then
echo "$f" #如果是目录就输出这个目录,$2为空格是用于区分父子目录
list_files "$1/$f"
else
echo "$f" #不是目录则输出$f结果
fi
done
}
list_files $1
[ -d "$1/$f" ]中,斜杠(/)用于将参数$1和变量$f连接起来,以构造一个路径,用于检查是否存在一个指定的目录。
例如,如果参数$1包含/home/user,变量$f包含mydir,则路径/home/user/mydir将被构造出来,
并用于检查是否存在一个名为mydir的目录。
函数库
- 函数库只包含函数的定义,脚本中既包含函数的定义也包括可执行的代码。
[root@gcc jiaoben1]#vim test8.sh
#!/bin/bash
jiafa () {
result=$[$1 + $2]
echo $result
}
jianfa () {
result=$[$1 - $2]
echo $result
}
chengfa () {
result=$[$1 * $2]
echo $result
}
chufa () {
if [ $2 -ne 0 ]
then
result=$[$1 / $2]
echo $result
else
echo "$2不能等于0!"
fi
}
----------------------------------------------------------------------------
[root@gcc jiaoben1]#vim test9.sh
#!/bin/bash
. /opt/jiaoben1/test8.sh
read -p "输入第一个参数值:" first
read -p "输入第二个参数值:" second
result1=`jiafa $first $second`
result2=`jianfa $first $second`
result3=$(chengfa $first $second)
result4=$(chufa $first $second)
echo $result1
echo $result2
echo $result3
echo $result4
--------------------------------------------------------
[root@gcc jiaoben1]#chmod +x test9.sh
[root@gcc jiaoben1]#./test9.sh
输入第一个参数值:20
输入第二个参数值:10
30
10
200
2
数组
数组定义方法
方法一:
数组名=(value0 valuel value2 …)
举例:
[root@loaclhost shuzu1]#arr=(1 2 3 4 5)
[root@loaclhost shuzu1]#echo ${arr[*]}
1 2 3 4 5
方法二:
数组名=( [0]=value [1]=value [2]=value …)
举例:
[root@loaclhost shuzu1]#arr=([0]=1 [1]=2 [2]=3)
[root@loaclhost shuzu1]#echo ${arr1[*]}
1 2 3
方法三:
列表名=“value0 valuel value2 …”
数组名=($列表名)
举例:
[root@loaclhost shuzu1]#list="1 2 3 4"
[root@loaclhost shuzu1]#arr2=($list)
[root@loaclhost shuzu1]#echo ${arr2[*]}
1 2 3 4
方法四:
数组名[0]=“value”
数组名[1]=“value”
数组名[2]=“value”
举例:
root@loaclhost shuzu1]#arr3[0]="1"
[root@loaclhost shuzu1]#arr3[1]="2"
[root@loaclhost shuzu1]#arr3[2]="3"
[root@loaclhost shuzu1]#echo ${arr3[*]}
1 2 3
数组包括的数据类型
- 数值类型
- 字符类型
- 使用" "或‘ ’定义
获取数组长度
[root@loaclhost shuzu1]#arr1=(1 2 3 4 5)
[root@loaclhost shuzu1]#echo ${#arr1[*]} #也就是数组中总共多少个元素
5
读取某下标赋值
[root@loaclhost shuzu1]#arr1=(1 2 3 4 5)
[root@loaclhost shuzu1]#echo ${arr1[0]} #获取索引为0的元素,即第一个元素
1
[root@loaclhost shuzu1]#echo ${arr1[3]} #获取索引为3的元素,即第四个元素
4
数组遍历
[root@loaclhost shuzu1]#vim a.sh
#!/bin/bash
arr5=(1 2 3 4 5)
for i in ${arr5[*]} 或 for i in ${arr5[@]} #使用*或者@表示都可以
do
echo $i
done
[root@loaclhost shuzu1]#chmod +x a.sh
[root@loaclhost shuzu1]#./a.sh #将数组中的元素列出来就叫数组遍历
1
2
3
4
5
数组切片
取数组中的某一段的元素的值
格式:
${数组名[@或*]}:起始位置(起始索引):长度
[root@loaclhost shuzu1]#arr1=(1 2 3 4 5)
[root@loaclhost shuzu1]#echo ${arr1[*]} #输出整个数组
1 2 3 4 5
[root@loaclhost shuzu1]#
[root@loaclhost shuzu1]#echo ${arr1[*]:0:2} #这里是从0索引开始获得往后两位元素的值
1 2
[root@loaclhost shuzu1]#echo ${arr1[*]:2:2} #获取从索引2开始往后的两位元素的值
3 4
数组替换
临时替换或者永久替换
格式:
$(数组名[@或*]/查找字符/替换字符}
[root@loaclhost shuzu1]#arr1=(1 2 3 4 5)
[root@loaclhost shuzu1]#echo ${arr1[*]/4/66} #将数组arr1中的元素4替换为66,这只是临时替换,并不是永久替换
1 2 3 66 5
[root@loaclhost shuzu1]#echo ${arr1[*]} #原来的数组中的元素的值并不会改变
1 2 3 4 5
[root@loaclhost shuzu1]#arr1=(${arr1[*]/4/66}) #如果想要永久替换的话,可通过重新赋值实现
[root@loaclhost shuzu1]#echo ${arr1[*]}
1 2 3 66 5
[root@test1 opt]# arr1[3]=44
[root@test1 opt]# echo ${arr1[*]}
1 2 3 44 5 6
删除数组
使用unset删除数组
[root@loaclhost shuzu1]#unset arr1
[root@loaclhost shuzu1]#echo ${arr1[*]}
删除数组中的某个索引对应的元素的值
[root@loaclhost shuzu1]#arr1=(1 2 3 4 5)
[root@loaclhost shuzu1]#echo ${arr1[*]}
1 2 3 4 5
[root@loaclhost shuzu1]#unset arr1[2] #删除索引2的对应的元素值
[root@loaclhost shuzu1]#echo ${arr1[*]}
1 2 4 5
追加数组中的元素
当想要在数组中原有的元素后面再追加一些元素的话,可以使用如下方法实现追加:
方法一:
[root@loaclhost shuzu1]#arr1=(1 2 3 4 5)
[root@loaclhost shuzu1]#echo ${arr1[*]}
1 2 3 4 5 #这是原始的数组中的各元素的值,一直到索引4结束
--------------------------------------------------------------------------------
[root@loaclhost shuzu1]#arr1[5]=6 #此时追加数组索引5对应的元素值为6
[root@loaclhost shuzu1]#echo ${arr1[*]}
1 2 3 4 5 6 #发现数组的索引5位置上的元素已经追加成功
方法二:
[root@loaclhost shuzu1]#arr1[${#arr1[*]}]=7
#当数组的元素非常多的时候,可以直接使用数组的长度作为自己将要追加的索引的值,这样就可以直接追加元素了。
因为原始数组的索引是从0开始的,所以用长度减去1就是原始数组的最后的以为索引值了,
那么自己将要添加的值应该是原始索引值的后一位,那显然就等于数组的长度值了。
[root@loaclhost shuzu1]#echo ${arr1[*]}
1 2 3 4 5 6 7
方法三:
[root@loaclhost shuzu1]#arr1=(1 2 3 4 5)
[root@loaclhost shuzu1]#echo ${arr1[*]}
1 2 3 4 5
---------------------------------------------------------
[root@loaclhost shuzu1]#arr1=("${arr1[@]}" 6 7)
[root@loaclhost shuzu1]#echo ${arr1[*]}
1 2 3 4 5 6 7
双引号不能省略,否则,当数组arr1中存在包含空格的元素时会按空格将元素拆分成多个。
不能将“@“替换为“*”,如果替换为"*",
不加双引号时与"@"的表现一致,加双引号时,会将数组arr1中的所有元素作为一个元素添加到数组中。
可以简单的理解为:用*号是作为一个整体,而用@还是单个的个体。
$*、$@不加双引号时表现一致;加双引号时,$*会将所有参数作为一个整体。
[root@loaclhost shuzu1]#abc=(1 2 3 4 5)
[root@loaclhost shuzu1]#echo ${abc[*]}
1 2 3 4 5
[root@loaclhost shuzu1]#abc=(${abc[@]} 6 7 8) #用@可以不加引号
[root@loaclhost shuzu1]#echo ${abc[*]}
1 2 3 4 5 6 7 8
[root@loaclhost shuzu1]#for i in ${abc[*]}
> do
> echo $i
> done
1
2
3
4
5
6
7
8
--------------------------------------------------------------------------------
[root@loaclhost shuzu1]#abc1=(1 2 3 4 5)
[root@loaclhost shuzu1]#echo ${abc1[*]}
1 2 3 4 5
[root@loaclhost shuzu1]#abc1=("${abc1[*]}" 6 7 8)
[root@loaclhost shuzu1]#echo ${abc1[*]}
1 2 3 4 5 6 7 8
[root@loaclhost shuzu1]#for i in "${abc1[@]}";do echo $i;done
1 2 3 4 5
方法四:
[root@loaclhost shuzu1]#arr1=(1 2 3 4 5)
[root@loaclhost shuzu1]#echo ${arr1[*]}
1 2 3 4 5
-----------------------------------------------
[root@loaclhost shuzu1]#arr1+=(11 22)
[root@loaclhost shuzu1]#echo ${arr1[*]}
1 2 3 4 5 11 22
待添加元素必须用“()"包围起来,并且多个元素用空格分隔
向函数传数组参数
1、如果将数组变量作为函数参数,函数只会取数组变量的第一个值。如下:
[root@loaclhost shuzu1]#vim b.sh
#!/bin/bash
test1 () {
echo "接受到的参数列表:$@"
abc2=$1
echo "新数组的值为:${abc2[*]}"
}
abc=(3 2 1 4 5)
echo "原始数组的值为:${abc[*]}"
test1 $abc #将数组变量作为函数的参数,只会取数组变量的第一个值
------------------------------------------------------------------------------------
[root@loaclhost shuzu1]#chmod +x b.sh
[root@loaclhost shuzu1]#./b.sh
原始数组的值为:3 2 1 4 5
接受到的参数列表:3
新数组的值为:3
2、解决这个问题则需要将数组变量的值分解成单个的值,
然后将这些值作为函数参数使用。在函数内部,再将所有的参数重新组合成一个新的数组变量。如下:
$*、$@不加双引号时表现一致;加双引号时,$*会将所有参数作为一个整体。
[root@loaclhost shuzu1]#vim c.sh
#!/bin/bash
test2 () {
abc1=($(echo $@)) 或者表示为 abc1=(`echo $@`)
echo "新数组的值为:${abc1[*]}"
}
abc=(`seq 1 10`)
test2 ${abc[*]} #将数组的值分解为单个的值
-------------------------------------------------
[root@loaclhost shuzu1]#chmod +x c.sh
[root@loaclhost shuzu1]#./c.sh
新数组的值为:1 2 3 4 5 6 7 8 9 10
3、从函数返回数组(调用新数组的元素进行函数运算)
举例1:(加法传参运算)
[root@loaclhost shuzu1]#vim c.sh
#!/bin/bash
test2 () {
abc1=(`echo $@`)
sum=0
for i in ${abc1[*]}
do
sum=$[$sum + $i]
done
echo "$sum"
}
abc=(3 2 1 4 5)
test2 ${abc[*]}
--------------------------------------------------------------
[root@loaclhost shuzu1]#chmod +x c.sh
[root@loaclhost shuzu1]#./c.sh
15
举例2:(乘法传参运算)
[root@loaclhost shuzu1]#vim c.sh
#!/bin/bash
test3 () {
abc1=(`echo $@`)
for ((i=0;i<=$[$# - 1];i++)) #$#是原始数组的元素个数,这里是取出新数组的索引值,不减的话就是一个字符串
do
abc1[$i]=$[${abc1[$i]} * 2] #这里是将每个原始索引对应的元素值乘以2传到新的数组中对应的索引的元素中去
done
echo "${abc1[*]}" #输出新的数组
}
abc=(1 2 3)
test3 ${abc[*]}
-----------------------------------------------------------------------
[root@loaclhost shuzu1]#./c.sh
2 4 6
数组排序算法:冒泡排序
类似气泡上涌的动作,会将数据在数组中从小到大或者从大到小不断的向前移动。
基本思想:
冒泡排序的基本思想是对比相邻的两个元素值,
如果满足条件就交换元素值,把较小的元素移动到数组前面,
把大的元素移动到数组后面(也就是交换两个元素的位置) ,
这样较小的元素就像气泡一样从底部上升到顶部
算法思路:
冒泡算法由双层循环实现,其中外部循环用于控制排序轮数,
一般为要排序的数组长度减1次,因为最后一次循环只剩下一个数组元素,不需要对比,
同时数组已经完成排序了。而内部循环主要用于对比数组中每个相邻元素的大小,
以确定是否交换位置,对比和交换次数随排序轮数而减少。
在实际应用中,冒泡排序适用于对小规模数据进行排序
#!/bin/bash
abc=(20 10 60 40 50 30) #定义一个数组
echo "原数组的排列顺序为:${abc[*]}"
length=${#abc[*]} #定义原数组的长度为length变量
for ((i=1;i<$length;i++)) #定义排序轮次
do
echo $i
for ((k=0;k<$length-i;k++)) #确定第一个元素的索引位置
do
first=${abc[$k]} #定义第一个元素的值
j=$[$k+1] #定义第二个元素的索引号
second=${abc[$j]} #定义第二个元素的值
if [ $first -gt $second ] #第一个元素和第二个元素比较,如果第一个元素比第二个元素大则互换
then
temp=$first #把第一个元素的值保存在临时变量temp中
abc[$k]=$second #把第二个元素的值赋给第一个元素
abc[$j]=$temp #把原第一个元素的值,赋给第二个元素
fi
done
done
echo "排序后数组的排列顺序为${abc[*]}" #输出排序后的数组
---------------------------------------------------------------------------------------
[root@loaclhost shuzu1]#./g.sh
原数组的排列顺序为:20 10 60 40 50 30
排序后数组的排列顺序为10 20 30 40 50 60