目录
一、shell函数
1、shell函数的概念
2、shell函数的用法
2.1 定义函数
2.2 调用函数
2.3 函数作用范围
2.3.1 调用函数的范围
2.3.2 全局作用域和局部作用域
3、 函数返回值
3.1 系统默认的返回值
3.2 return语句
4、函数传参
5、查看函数列表
6、删除函数
7、 函数递归
7.1 函数递归死循环
7.1.1 模拟死循环
7.1.2 fork炸弹
7.2 阶乘
二、shell数组
1、shell 数组的概念
2、shell 数组的用法
2.1 声明数组
2.1.1 普通数组
2.1.2 关联数组
2.2 定义数组
2.3 读取数组信息
2.3.1 获取数组长度
2.3.2 获取数组列表
2.3.3 读取下标赋值
2.3.4 读取数组下标
2.4 遍历数组
2.5 元素的切片
2.6 元素的临时替换
2.7 删除数组
3、数组最值
3.1 数组的最大值
3.2 数组的最小值
3.3 随机数组的最大最小值
4、冒泡排序
5、随机点名
在shell脚本中,函数是一组被命名的命令序列,可以在脚本中多次调用。
使用函数可以将一段常用的代码逻辑封装起来,提高代码的可读性和重用性
函数的组成:函数名和函数体
帮助:help function
先定义函数,再调用函数
#定义函数方法一:
function 函数名 {
命令序列
}
#定义函数方法二:
函数名 () {
命令序列
}
#定义函数方法三
function 函数名 () {
命令序列
}
#在定义函数后添加函数名,即调用函数
函数名
函数调用虽然可以出现在脚本的任何位置,但前提必须是在调用之前已经定义了相应的函数
局部作用域:函数内部使用local
关键字声明的变量默认为局部变量,只在函数内部有效。这意味着函数内的变量不会影响到函数外部的同名变量,也不会被函数外部的代码访问到
#local关键字用于声明局部变量
全局作用域:如果在函数内部没有使用local
关键字声明变量,则该变量默认将成为全局变量,在整个脚本中都可见和访问。全局变量可以在函数内部创建,并且可以在函数内部和外部使用
函数在Shell脚本中仅在当前Shell环境中有效,而Shell脚本中变量默认全局有效
#检测输入的ip地址是否符合规范
#!/bin/bash
IPADDRESS () {
read -p "请输入IP地址: " ip_address
# 定义IP地址的正则表达式模式
ip_pattern="^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"
# 使用正则表达式进行匹配
if [[ $ip_address =~ $ip_pattern ]]; then
echo "IP地址格式正确"
else
echo "IP地址格式错误"
fi
}
IPADDRESS
注:
函数一结束就取返回值,因为$?变量只返回执行的最后一条命令的退出状态码
系统默认只要能执行脚本,无论脚本使用是否出错,使用
$?
特殊变量获取返回值都是0值如果想通过返回值向调用者提供错误或正确的结果,就需要使用
return
语句来指定返回值
使用return
语句来指定函数的返回值。可以将任意整数值作为返回值,但退出状态码必须是0~255,超出时值将为除以256取余
通常用非零值表示错误或异常情况,而零表示成功或正常情况
函数传参是指将参数传递给一个函数来供函数内部使用
当你定义一个函数时,可以在函数名后的括号内指定参数列表。这些参数将被视为函数的输入,并且可以在函数体内使用
函数传参的目的是让函数能够接受外部提供的数据或信息,以便在函数内部进行处理和操作。通过传递参数,可以使函数更加灵活和通用,可以根据不同的输入执行相同的操作
declare -F #查看当前已定义的函数名
declare -f #查看当前已定义的函数定义
declare -f 函数名 #查看当前已定义的函数名定义
declare -F 函数名 #查看指定当前已定义的函数名
unset 函数名 #删除定义过的函数
函数递归是指函数调用自身的过程。递归函数可以通过重复调用自身来解决复杂的问题,将大问题分解成更小的子问题,并通过递归调用来解决这些子问题
函数递归指的是在函数内部调用自身的过程,如果不正确使用,函数递归可能会导致死循环,即函数无限地调用自身,直到耗尽系统资源或达到某个限制
[root@localhost ~]# wx () { echo $i;echo "run fast";let i++;wx; }
[root@localhost ~]# wx
fork 炸弹是一种恶意程序,它的内部是一个不断在 fork 进程的无限循环,实质是一个简单的递归程序
由于程序是递归的,如果没有任何限制,这会导致这个简单的程序迅速耗尽系统里面的所有资源
[root@localhost ~]#bomb() { bomb | bomb & }; bomb
[root@localhost ~]# :(){ :|:& };:
阶乘是一个正整数的乘积,从该正整数开始递减,直到1。通常用符号"!"表示。例如,n的阶乘可以表示为n!,计算方式为n! = n * (n-1) * (n-2) * ... * 3 * 2 * 1。其中,0的阶乘定义为1
方法一:利用函数递归法求阶乘
使用函数递归来计算阶乘,可以通过重复调用自身来解决阶乘这个复杂的问题
#接受一个参数 $num,表示要计算阶乘的数。函数内部使用条件语句判断基本情况,即当输入的数小于等于1时,直接返回1。否则,它会递归地调用自身来计算$num-1的阶乘,并将结果与$num相乘,然后返回结果
#!/bin/bash
jc () {
read -p "请输入一个正整数:" num
if [ $num -eq 1 ];then
echo 1
else
local temp=$[$num-1]
local result=`jc $temp`
echo $[$num*result]
fi
}
jc $num
bash $0
方法二:利用for循环求阶乘
#!/bin/bash
read -p "请输入一个正整数:" num
sum=1
i=1
for i in `seq $num`
do
let sum=$[sum*i]
done
echo $sum
bash $0
注:
基本情况:递归函数必须包含一个基本情况,即函数停止调用自身的条件。如果没有基本情况,递归函数将无限循环下去,导致栈溢出
递归调用:在函数内部,通过调用相同的函数来实现递归。递归调用必须在满足基本情况之前发生,以确保递归能够终止
在Shell脚本中,数组是一种特殊的变量类型,用于存储一系列的值(元素),你可以使用括号来创建和初始化数组
变量和数组:
数组的类型:
数组名和索引:
普通数组是一系列按照索引顺序排列的元素集合。在shell脚本中,普通数组的索引从0开始。可以通过索引来访问数组中的元素
#申明普通数组,shell脚本默认普通数组,可以不使用命令申明直接使用普通数组
declare -a 数组名
关联数组是根据键值对存储的数组,也被称为字典或哈希表。在shell脚本中,关联数组的键可以是任意字符串,而不仅仅是数字。可以通过键来访问数组中的值
#申明关联数组,使用关联数组前必须使用命令申明
declare -A array_name
方法一:一次赋值全部元素
数组名=(value0 value1 value2 value3 ......)
方法二:一次只赋值一个元素(为了方便修改或追加元素)
数组名[0]="value1"
数组名[1]="value2"
数组名[2]="value3"
……………………………
方法三:综合方法一和方法二,赋值特定元素
数组名=([0]=value [1]=value1 [2]=value2 ......)
方法四:元素赋值给列表,引用列表加入组
变量名="value0 value1 value2 value3 ...... "
数组名= $变量名
方法五:交互式
read -a 数组名
#在数组前添加#,获取数组长度
[root@localhost data]#echo ${#a[@]}
[root@localhost data]#echo ${#a[*]}
[root@localhost data]#echo ${a[@]}
[root@localhost data]#echo ${a[*]}
[root@localhost data]#echo ${a[1]}
[root@localhost data]#echo ${a[2]}
[root@localhost data]#echo ${!a[@]}
[root@localhost data]#echo ${!a[*]}
拓展:
不知道数组的下标,如何追加元素?
#使用数组总长度为下标进行赋值
[root@localhost data]#a[${#a[@]}]=6
遍历数组意味着逐个访问数组中的元素,这通常用于对数组中的每个元素执行相同的操作,比如打印它们或者进行某种计算
#!bin/bash
a=(10 20 30 40 50)
for i in ${a[@]} #挨个赋值,循环五次
do
echo i=$i
done
可以使用数组切片来获取数组的子集,即从一个数组中获取一部分元素的操作
这种操作允许你创建一个新的数组,其中包含原始数组中特定范围的元素
${a[*]:n:m} #提取从索引下标n开始的m个元素
${a[*]:n} #提取从索引下标n开始的所有元素
echo ${数组名[@]/原始字符/替换字符} #只是象征性的更换,实际并没有改变原有内容
unset 数组名 #删除数组
unset 数组名[下标] #删除指定的数组元素
先定义了一个包含一些整数的数组(交互输入)。然后,假设数组的第一个元素是最大值。接下来,我们使用for
循环遍历数组中的每个元素,并将其与当前的最大值进行比较。如果当前元素大于最大值,则更新最大值。最后,打印出最大值
循环次数小于数组长度(即小于数值元素个数,比较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"
先定义了一个包含一些整数的数组(交互输入)。然后,假设数组的第一个元素是最小值。接下来,我们使用for
循环遍历数组中的每个元素,并将其与当前的最小值进行比较。如果当前元素小于最小值,则更新最小值。最后,打印出最小值
循环次数小于数组长度(即小于数值元素个数,比较n-1次),每次加1
#!/bin/bash
read -p "请输入数组的整数值并以空格隔开:" num
a=($num)
l=${#a[@]}
min=${a[0]}
for ((i=0;i<$l-1;i++))
do
if [[ $min -ge ${a[$i+1]} ]];then
min=${a[$i+1]}
fi
done
echo "最小值为:$min"
首先生成一个包含10个随机整数的数组。然后,通过遍历数组,使用两个变量max
和min
来记录当前的最大值和最小值。最后,打印出最大值和最小值
#!/bin/bash
for i in {0..10}
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"
echo "最小值为:$min"
冒泡排序类似气泡上涌的动作,会将数据在数组中从小到大或者从大到小不断的向前移动
对比相邻的两个元素值,如果满足条件就交换元素值,把较小的元素移动到数组前面,把大的元素移动到数组后面(也就是交换两个元素的位置)这样较小的元素就像气泡一样从底部上升到顶部
冒泡算法由双层循环实现,其中外部循环用于控制排序轮数,一般循环次数小于数组长度(即小于数值元素个数,比较n-1次),每次加1。而内部循环主要用于对比数组中每个相邻元素的大小,以确定是否交换位置对比和交换次数随排序轮数而减少
第一轮比较(n个数):找出最大值,将最大值放到最后,比n-1次
第二轮比较(n-1个数):找出最大值,将最大值放到最后,比n-2次
第三轮比较(n-2个数):找出最大值,将最大值放到最后,比n-3次
……
最后一轮比较(最后两个数):找出最大值,将最大值放到最后,比1次
#!/bin/bash
a=(1 5 2 0 6 4 8)
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
echo ${a[@]}
冒泡排序随机十个数
#!/bin/bash
for i in {0..10}
do
a[$i]=$RANDOM
done
l=${#a[@]}
for ((i=1;i
要实现随机点名的功能,可以使用Shell脚本中的随机数生成函数和数组来实现
#!/bin/bash
namefile="/data/name.txt"
linenum=$(sed -n '$=' $namefile)
while :
do
clear
tmp=$(sed -n "$[RANDOM%linenum+1]p" $namefile)
echo -e "\E[32m 随机点名(ctrl+c停止): \E[0m"
echo -e "\E[32m########################\E[0m"
echo -e "\E[32m# #\E[0m"
echo -e "\E[32m $tmp \E[0m"
echo -e "\E[32m# #\E[0m"
echo -e "\E[32m########################\E[0m"
sleep 0.03
done