目录
一、函数
1. 概述
在Shell脚本中,函数是一种可重复使用的代码块,可以在脚本中多次调用。函数可以接受参数,执行一些操作,并且可以返回一个值。函数的定义和调用都遵循一定的语法规则。需要注意的是函数必须先定义才可以使用。
2. 定义调用函数
【1】
function 函数名 {
命令序列 #函数要实现的功能代码
}
【2】
函数名() {
命令序列 #函数要实现的功能代码
}
【3】
function 函数名 (){
命令序列 #函数要实现的功能代码
}
示例:
[root@localhost ~]# vim hello.sh
#!/bin/bash
function hello { #定义函数名为hello,它不接受任何参数
echo "Hello, World!"
}
hello #调用函数
[root@localhost ~]# bash hello.sh #执行的操作是输出字符串"Hello, World!"
Hello, World!
3. 查看函数列表
declare -F:查看当前已定义的函数名
declare -f:查看当前已定义的函数定义
declare -f func_name:查看当前已定义的函数名定义
declare -F func_name:查看指定当前已定义的函数名
4. 删除函数
[root@localhost ~]# hello (){ echo "Hello, World"; } #定义函数
[root@localhost ~]# hello #调用函数
Hello, World
[root@localhost ~]# unset hello #unset func_name
[root@localhost ~]# hello
bash: hello: 未找到命令...
5. 建立函数文件并添加颜色
创建一个存储函数的文件,可以在其中定义需要的函数,使用时运行脚本即可。
示例:检测当前系统linux版本
[root@localhost ~]# cat func
#!/bin/bash
os () { #定义函数
if grep -q centos /etc/os-release;then
echo "centos"
elif grep -q ubuntu /etc/os-release;then
echo "ubuntu"
else
echo "不支持"
fi
}
color () { #定义函数
red="echo -e \E[31m"
green="echo -e \E[32m"
end="\E[0m"
}
[root@localhost ~]# vim os.sh
#!/bin/bash
. /root/func
os #调用函数
color #调用函数
$green安装成功$end #调用变量
[root@localhost ~]# bash os.sh
centos
安装成功 #文字呈现绿色
6. 函数返回值
函数的返回值可以在调用函数时被捕获并使用。通常可以使用赋值操作符将函数的返回值保存在变量中,以便后续使用。赋值范围0~255。 return 100提前退出函数并指定返回值100,即exit 100。
示例:
[root@localhost ~]# num () { [ 1 -eq 0 ] || echo "1不等于0"; };echo $?
0
[root@localhost ~]# num () { [ 1 -eq 0 ] || echo "1不等于0;return 1};echo $?
1
#return 1指定函数返回值
应用:判断IP地址是否合法
[root@localhost ~]# vim os.sh
ip () {
read -p "请输入ip:" host
[[ $host =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] || echo "${host}不合法";return 1;
}
ip
[root@localhost ~]# bash 1.sh
请输入ip:192.168.190.1000
192.168.190.1000不合法
[root@localhost ~]# echo $? #如果不修改函数返回值,即使ip不合法$?依然可以返回0
1
使用原则:
- 函数一结束就取返回值,因为$?变量只返回执行的最后一条命令的退出状态码
- 退出状态码必须是0~255,超出时值将为除以256取余
7. 函数传参
函数传参是指将参数传递给一个函数来供函数内部使用。当你定义一个函数时,可以在函数名后的括号内指定参数列表。这些参数将被视为函数的输入,并且可以在函数体内使用。函数传参的目的是让函数能够接受外部提供的数据或信息,以便在函数内部进行处理和操作。通过传递参数,可以使函数更加灵活和通用,可以根据不同的输入执行相同的操作。
[root@localhost ~]# vim c.sh
#!/bin/bash
h () {
echo "参数是:"$1 $2 $3
}
h $1 $2 $3
[root@localhost ~]# bash c.sh 1 2 3
参数是:1 2 3
特别情况介绍:
[root@localhost ~]# vim c.sh
#!/bin/bash
h () {
echo "参数是:"$1 $2 $3
}
h $2 $1 $3
[root@localhost ~]# bash c.sh 1 2 3
参数是:2 1 3
h $2 $1 $3中:对于函数而言依然是$1 $2 $3;对于脚本位置变量所对应的是2 1 3
8. 函数变量的作用范围
[root@localhost ~]# name=zhou
[root@localhost ~]#
[root@localhost ~]# name=zhang
[root@localhost ~]# func1 () { name=li;echo $name; } #函数的变量会影响当前环境
[root@localhost ~]# func1
li
[root@localhost ~]# echo $name
li
[root@localhost ~]# name=zhang
[root@localhost ~]# func1 () { local name=li;echo $name; } #local,锁定在本地
[root@localhost ~]# func1
li
[root@localhost ~]# echo $name
zhang
9. 函数递归
递归是指一个函数调用自身的过程,需要注意的是,递归函数可能会导致性能问题,因为每次调用都会创建一个新的函数帧。
9.1 模拟死循环
死循环一直调用自身开进程,模拟病毒
[root@localhost ~]# func () { echo $i;echo "run fast";let i++;func; }
[root@localhost ~]# func
fork炸弹
bomb() { bomb | bomb & }; bomb
9.2 阶乘
阶乘是一个自然数与小于它的自然数的乘积。通常用感叹号表示,例如n的阶乘用n!表示,定义为n!=n*(n-1)(n-2)...321。
① for循环写法
#!/bin/bash
read -p "请输入一个正整数:"num
sum=1
i=1
for i in `seq $num`
do
let sum=$[sum*i]
done
echo $sum
[root@localhost ~]# bash for.sh
请输入一个正整数:5
120
② 函数写法
#!/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 "请输入一个正整数:" num
fact $num
[root@localhost ~]# bash h.sh
请输入一个正整数:5
120
原理:当输入数字时,和1比较,如果等于则输出1;如果不等于,求比自己小1数字的阶乘;如果依然不等于1,则继续求比自己小1数字的阶乘,直到等于1为止。最后根据上一个数字的阶乘结果与自己相乘得到自己的阶乘。比如5的阶乘计算流程:知道1的阶乘,计算2的阶乘为2*1;知道2的阶乘,计算3的阶乘为3*2;知道3的阶乘,计算4的阶乘为4*6;知道4的阶乘,计算5的阶乘为5*24。
二、数组
1. 数组的定义
在Shell脚本中,数组是一种特殊的变量类型,用于存储一系列的值(元素),你可以使用括号来创建和初始化数组。
变量和数组:
- 变量:用一个固定的字符串,代替一个不固定字符串
- 数组:用一个固定的字符串,代替多个不固定字符串
数组的类型:
- 普通数组:只能使用整数作为数组索引
- 关联数组:可以使用字符串作为数组索引
数组名和索引:
- 索引的编号(下标)从0开始,属于数值索引
- 索引可支持使用自定义的格式,而不仅是数值格式,即为关联索引,bash 4.0版本之后开始支持bash的数组支持稀疏格式(索引不连续)
2. 声明数组
declare -a 数组名:申明普通数组,可以不申明直接使用
declare -A 数组名:申明关联数组,必须要申明
[root@localhost ~]# declare -A lisi #申明关联数组
[root@localhost ~]# lisi[people]=3
[root@localhost ~]# lisi[address]=china
[root@localhost ~]# lisi[mail]=qq
[root@localhost ~]# echo ${lisi[people]} #调用
3
[root@localhost ~]# echo ${lisi[mail]}
qq
[root@localhost ~]# echo ${lisi[address]}
china
3. 定义数组
方法一:一次赋值全部元素
[root@localhost ~]# a=(1 2 3 4 5)
[root@localhost ~]# echo ${a[@]}
1 2 3 4 5
方法二:一次只赋值一个元素(目的为了修改追加元素)
[root@localhost ~]# b[0]=6
[root@localhost ~]# b[1]=7
[root@localhost ~]# b[2]=8
[root@localhost ~]# echo ${b[@]}
6 7 8
方法三:综合一二,赋值特定元素
[root@localhost ~]# c=([0]=9 [1]=10 [2]=11)
[root@localhost ~]# echo ${c[@]}
9 10 11
方法四:元素赋值给变量,引用变量加入组
[root@localhost ~]# num=`seq 12 15`
[root@localhost ~]# d=$num
[root@localhost ~]# echo ${d[@]}
12 13 14 15
方法五:交互式,基本格式read -a 数组名
[root@localhost ~]# read -a num
16 17 18
[root@localhost ~]# echo ${num[@]}
16 17 18
4. 获取数组信息
4.1 获取数组长度
[root@localhost ~]# a=(1 2 3 4 5)
[root@localhost ~]# echo ${#a[*]}
5
[root@localhost ~]# echo ${#a[@]}
5
4.2 获取数据列表
[root@localhost ~]# echo ${a[*]}
1 2 3 4 5
[root@localhost ~]# echo ${a[@]}
1 2 3 4 5
4.3 读取下标赋值
[root@localhost ~]# echo ${a[1]}
2
[root@localhost ~]# echo ${a[4]}
5
4.4 读取下标
[root@localhost ~]# echo ${!a[*]}
0 1 2 3 4
不知道下标,如何追加元素?
[root@localhost ~]# a[${#a[@]}]=6
[root@localhost ~]# echo ${a[*]}
1 2 3 4 5 6
#下标从0开始,先查询长度,再以长度为下标赋值
5. 数组遍历
遍历数组意味着逐个访问数组中的元素,这通常用于对数组中的每个元素执行相同的操作,比如打印它们或者进行某种计算。
#!bin/bash
a=(1 2 3 4 5)
for i in ${a[@]} #挨个赋值,循环五次
do
echo i=$i
done
[root@localhost ~]# bash b.sh
i=1
i=2
i=3
i=4
i=5
6. 元素的切片
可以使用数组切片来获取数组的子集,即从一个数组中获取一部分元素的操作。这种操作允许你创建一个新的数组,其中包含原始数组中特定范围的元素。
${a[*]:n:m} 提取从索引下标n开始的m个元素
${a[*]:n} 提取从索引下标n开始的所有元素
[root@localhost ~]# a=(1 2 3 4 5 6)
[root@localhost ~]# echo ${a[@]:3:2} #跳过前三个取后两个
4 5
[root@localhost ~]# echo ${a[@]:3} #跳过前三个
4 5 6
7. 元素的临时替换
echo ${数组名[@]/查找字符/替换字符} 只是象征性的更换,实际并没有改变原有内容
echo ${数组名[@]} 并不会替换数组原有内容
[root@localhost ~]# a=(1 2 3 4 5 6)
[root@localhost ~]# echo ${a[@]/6/10}
1 2 3 4 5 10
[root@localhost ~]# echo ${a[@]}
1 2 3 4 5 6
8. 数组删除
[root@localhost ~]# a=(1 2 3 4 5 6)
[root@localhost ~]# unset a[5];echo ${a[@]} #删除指定元素
1 2 3 4 5
[root@localhost ~]# unset a;${a[@]} #删除整个数组
[root@localhost ~]#
9. 数组最值
9.1 固定数组最大值
求数值最大值:假设第一个数最大,与第下一个数作比较,如果小于第二个数,则第二个数上位“临时最大值”,继续同下一个数比较;如果大于第二个数,则保持“临时最大值”同下一个数做比较。循环次数小于数组长度(即小于数值元素个数,比较n-1次),每次加1。具体如下:
#!/bin/bash
read -p "请输入数组值以空格隔开:" num
a=($num)
l=${#a[@]}
max=${a[0]}
for ((i=0;i<$l;i++))
do
if [[ $max -lt ${a[$i+1]} ]];then
max=${a[$i+1]}
fi
done
echo $max
[root@localhost ~]# bash max.sh
请输入数组值以空格隔开:1 2 7 8 5
8
9.2 随机数组最大最小值
随机生成10个数值,求最大值和最小值。没有比较时,第一个数既是最大值也是最小值,将其作为临时最大值;如果后面的数大于最大值,后面的数即为最大值;如果后面的数小于最大值,最大值保持不变。
#!/bin/bash
for i in {0..9}
do
a[$i]=$RANDOM
[ $i -eq 0 ] && min=${a[0]} && max=${a[0]}
[ ${a[$i]} -gt $max ] && max=${a[$i]}
[ ${a[$i]} -lt $max ] && min=${a[$i]}
done
echo ${a[@]}
echo max=$max
echo min=$min
[root@localhost ~]# bash maxmin.sh
4761 13743 11963 5884 7064 18659 25427 4096 11666 32114
max=32114
min=11666
10. 数组冒泡排序
冒泡排序通过多次遍历数组,比较相邻元素并交换它们的位置,逐渐将较大(或较小)的元素推向数组的末尾。这个过程会重复进行,直到整个数组排序完成。
中心思想:定义两个元素和一个临时变量,如果第一个比第二个大,互换,并把第一个元素值保留在临时变量中;把第二个元素值赋给第一个元素;把临时变量赋给第二个元素。
外循环:从第一轮开始,轮次小于数组长度(比较轮次)
内循环:数组的长度减去轮次(找数)
第一轮比较(n个数):找出最大值,将最大值放到最后,比n-1次
第二轮比较(n-1个数):找出最大值,将最大值放到最后,比n-2次
第三轮比较(n-2个数):找出最大值,将最大值放到最后,比n-3次
……
最后一轮比较(两个数):找出最大值,将最大值放到最后,比1次
#!/bin/bash
a=(1 3 5 2 4 6)
l=${#a[@]}
for ((i=1;i<$l;i++)) #比较轮次
do
for ((j=0;j<$l-i;j++)) #找数
do
first=${a[$j]} #定义第一个数
k=$[j+1] #定义变量代表后一个数
second=${a[$k]} #第二个数赋值
if [ $first -gt $second ];then
temp=$first #第一个数作为临时值
a[$j]=$second #后一个数的值赋给前一个
a[$k]=$temp #临时值赋给后一个数
fi
done
done
[root@localhost ~]# bash mp.sh
1 2 3 4 5 6