目录
前言
函数的作用
- 将命令序列按格式写在一起
- 可方便重复使用命令序列
使用函数可以避免代码重复
使用函数可以将大的工程分割为若干小的功能模块,代码的可读性更强
一、函数
1.1 函数定义
方式一:
function 函数名 {
命令序列
}
示列:
#!/bin/bash
function abc { #使用function进行函数定义
read -p "请输入:" a
a=$[$a*2]
return $a #return表示退出函数并返回一个退出值,脚本中可以用 $? 变量显示该值,我们执行的返回码用于判断命令是否执行成功
}
abc
echo $? #退出状态码必须是0-255,超出时值将为除以256取余
# 调用函数并将其返回值保存在result变量中
abc
result=$?
方式二:
函数名 () {
命令序列
}
示列:
#!/bin/bash
abc () {
read -p "请输入:" a
a=$[$a*2]
echo $a
}
abc #直接使用函数名进行运算,这里加了abc就会输出两次
或者
result=$(abc)
echo $result
1.2 函数返回值
return表示退出函数并返回一个退出值,脚本中可以用 $? 变量显示该值
使用原则:
- 函数一结束就取回返回值,因为 $? 变量只返回执行的最后一条命令的退出状态码
- 退出状态码必须是0-255,超出时值将为除以256取余 exit 0
- return和exit的区别在于它们的作用范围:return只作用于函数内部,而exit作用于整个脚本。如果想从函数中返回一个值并继续执行脚本,应该使用return语句;如果想在任何时候结束整个脚本的执行,应该使用exit语句。
- 在 Shell 脚本中,return 语句用于指定函数的退出状态码(返回码)。约定俗成地,
返回码为 0 表示成功,非零值表示失败或错误。 - 返回码的具体含义是由你自己定义的,只要符合约定即可。你可以根据自己的需要定义不同的返回码和其对应的含义。
- Shell中的 return 命令通常用于在函数内部控制流程,并将状态码或结果传递给调用者。
它的主要作用是告诉调用者函数的执行状态或结果,并不像其他语言中的 return 语句那样直接返回函数结果。 - Shell中的 return 命令主要用于控制函数的退出状态码或返回结果,以便在函数调用后进行相应的处理。
虽然Shell中的 return 不同于其他语言中的返回语句,但在Shell脚本编程中,它仍然具有一定的作用和用途。 - 在编写 Shell 脚本时,根据函数的执行结果来设置适当的返回码是一种良好的编程习惯,
它可以帮助调用者根据返回码来判断函数的执行状态,并根据需要采取相应的措施 - 在 Shell 函数中,可以使用 exit 语句来终止函数的执行并返回一个退出状态码。
当函数执行到 exit 语句时,整个脚本将立即停止执行,并将指定的退出状态码返回给调用者
1.3 函数传参
在Shell中,调用函数时可以向其传递参数。
在函数体内部,通过 $n 的形式来获取参数的值,
例如,$1表示第一个参数,$2表示第二个参数…即使用位置参数来实现参数传递。
示列:
#!/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
#!/bin/bash
sum1 () {
sum=$[$1 + $2]
echo $sum
}
sum1 $1 $2
---------------------------------------------------------------------------
[root@gcc jiaoben1]#./test5.sh 10 20
30
1.4 函数变量的作用范围
函数在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"
1.5 阶乘函数
阶乘(factorial)是基斯顿·卡曼(Christian Kramp, 1760 – 1826)于1808年发明的运算符号。
阶乘,也是数学里的一种术语。
阶乘指从1乘以2乘以3乘以4一直乘到所要求的数。 6 1*2*3*4*5*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!
示列:
#!/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。
1.6 递归目录函数
遍历目标目录下的所有目录及子目录
#!/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的目录。
二、数组
2.1 数组基本介绍
- arr=(30 20 10 60) #数组中的元素值其中:30为索引0、20为索引1、10为索引2、60为索引3、以此类推,不过注意索引是从0开始的,不要弄混肴
- 如果数组中有三个参数:1 2 3
- 运行数组脚本后
"$*" 为 “1 2 3” (作为一个整体使用)
"$@" 为“1” “2” “3” (分别作为单个的个体使用)
"$#" 为3 (表示参数的数量,也可以叫做长度
- 运行数组脚本后
echo "使用 \"${arr[*]}\" 进行展开:"
for item in "${arr[*]}"; do
echo $item
done
echo "使用 \"${arr[@]}\" 进行展开:"
for item in ${arr[@]}; do
echo $item
done
2.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
2.3 数组包括的数据类型
- 数值类型
- 字符类型
- 使用" "或‘ ’定义
2.4 获取数组长度
[root@loaclhost shuzu1]#arr1=(1 2 3 4 5)
[root@loaclhost shuzu1]#echo ${#arr1[*]} #也就是数组中总共多少个元素
5
2.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
2.6 数组遍历
[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
2.7 数组切片
取数组中的某一段的元素的值
格式:
${数组名[@或*]}:起始位置(起始索引):长度
[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
2.8 数组替换
临时替换或者永久替换
格式:
$(数组名[@或*]/查找字符/替换字符}
[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
2.9 删除数组
使用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
2.10 追加数组
方法一:
[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]#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
待添加元素必须用“()"包围起来,并且多个元素用空格分隔
2.11 向函数传数组参数
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、从函数返回数组(调用新数组的元素进行函数运算)
#!/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
三、冒泡排序
类似气泡上涌的动作,会将数据在数组中从小到大或者从大到小不断的向前移动。
基本思想:
冒泡排序的基本思想是对比相邻的两个元素值,
如果满足条件就交换元素值,把较小的元素移动到数组前面,
把大的元素移动到数组后面(也就是交换两个元素的位置) ,
这样较小的元素就像气泡一样从底部上升到顶部
算法思路:
冒泡算法由双层循环实现,其中外部循环用于控制排序轮数,
一般为要排序的数组长度减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
详细动画讲解可看此链接视频:冒泡排序法_哔哩哔哩_bilibili