Shell编程之循环语句与函数
文章目录
一、for循环语句
1、for语句的结构
使用 for 循环语句时,需要指定一个变量及可能的取值列表,针对每个不同的取值重复执行相同的命令序列,直到变量值用完退出循环。在这里“,取值列表”称为 for 语句的执行条件,其中包括多个属性相同的对象,需要预先指定(如通讯录、IP 黑名单)。
for 语句的执行流程:首先将列表中的第一个取值赋给变量,并执行 do…done 循环体中的命令序列;然后将列表中的第二个取值赋给变量,并执行循环体中的命令序列……依此类推,直到列表中的所有取值用完,最后将跳至 done 语句,表示结束循环。
for语句的应用示例
示例1
批量添加用户用户名存放在users.txt文件中,每行一个
初始密码均设为123456
验证脚本
先新建一个文件存放用户名users.txt
#!/bin/bash
name=`cat /root/shell/users.txt`
for name in $name
do
echo "新增用户: $name"
useradd $name
echo "123456" | passwd --stdin $name &>/dev/null
chage -d 0 $name
done
示例2
根据IP地址检查主机状态
IP地址存放在ipadds.txt文件中,每行一个使
用ping命令检测各主机的连通性
先新建一个文件存放需要连接的ip地址
#!/bin/bash
ip=`cat /root/shell/ipadds.txt`
for ip in $ip
do
echo "检查连通性: $ip"
ping -c 5 -i 0.2 -W 2 $ip &>/dev/null
#判断ping的结果
if [ $? -eq 0 ] then
echo "$ip 已连通"
else
echo "$ip 不能连通 请检查配置 "
fi
done
拓展
#!/bin/bash
#统计某个目录中所有文件的个数和大小
read -p "请指定目录:" dir
if [ ! -d $dir ] ; then
echo "指定的不是不是目录,程序结束"
exit
fi
#遍历目录
COUNT=0
SIZE=0
TOTAL=0
for file in `ls $dir`
do
if [ -f "$dir"/"$file" ] ; then
COUNT=$((COUNT+1))
SIZE=`stat "$dir"/"$file" | sed -n "2p" | awk '{print $1}' | sed "s/[^0-9]//g"`
TOTAL=$((TOTAL+SIZE))
fi
done
echo "$dir 目录中的文件个数: $COUNT"
echo "$dir 目录中的文件大小: $TOTAL 字节"
二、使用 while 循环语句
重复测试某个条件,只要条件成立则反复执行
for 循环语句非常适用于列表对象无规律,且列表来源已固定(如某个列表文件)的场合。而对于要求控制循环次数、操作对象按数字顺序编号、按特定条件执行重复操作等情况,则更适合使用另外一种循环——while 语句。
1、while 语句的结构
使用 while 循环语句时,可以根据特定的条件反复执行一个命令序列,直到该条件不再满足时为止。在脚本应用中,应该避免出现死循环的情况,否则后边的命令操作将无法执行。因此,循环体内的命令序列中应包括修改测试条件的语句,以便在适当的时候使测试条件不再成立,从而结束循环。
while 语句的执行流程:首先判断 while 后的条件测试操作结果,如果条件成立,则执行 do…done 循环体中的命令序列;返回 while 后再次判断条件测试结果,如果条件仍然成立,则继续执行循环体;再次返回到 while 后,判断条件测试结果……如此循环,直到 while后的条件测试结果不再成立为止,最后跳转到 done 语句,表示结束循环使用while循环语句时有两个特殊的条件测试操作,即 true(真)和 false(假)使用 true 作为条件时,表示条件永远成立,循环体内的命令序列将无限执行下去,除非强制终止脚本(或通过 exit 语句退出脚本);反之,若使用 false 作为条件,则循环体将不会被执行。这两个特殊条件也可以用在 if 语句的条件测试中。
while语句应用示例
示例1
批量添加用户
用户名称以stu开头,按数字顺序进行编号
一共添加20个用户,即stu1、stu2、……、stu20
初始密码均设为123456
#!/bin/bash
i=1
while [ $i -le 20 ]
do
echo "添加用户:stu$i "
useradd stu$i
echo "123456" | passwd --stdin stu$i &>/dev/null
chage -d 0 stu$i
let i++
done
示例2
从2000年到2030年哪些是闰年,哪些是平年闰年
判断条件:能被4整除但不能被100整除、或直接400整除的年份
#!/bin/bash
i=2000
while [ $i -le 2030 ]
do
result1=$((i%4))
result2=$((i%100))
result3=$((i%400))
if [ $result1 -eq 0 -a $result2 -ne 0 -o $result3 -eq 0 ] ; then
echo "$i 是闰年..."
else
echo "$i 是平年"
fi
let i++
done
三、until 循环语句
1、until 语句的结构
重复测试某个条件,只要条件不成立则反复执行
until 循环与 while 循环类似,while 循环能实现的脚本 until 同样也可以实现,但区别是while 循环在条件为真实继续执行循环,而 until 则是在条件为假时执行循环。
until 语句的执行流程:首先判断 until 后的条件测试操作结果,如果条件不成立,则执行 do…done 循环体中的命令序列;返回 until 后再次判断条件测试结果,如果条件仍然不成立,则继续执行循环体;再次返回到 until 后,判断条件测试结果……如此循环,直到 until后的条件测试结果成立为止,最后跳转到 done 语句,表示结束循环
until语句应用示例
示例1
计算1~50的和值
通过循环累加的方式计算1~50的和值
#!/bin/bash
i=1
until [ $i -gt 10 ]
do
echo "当前数字:$i"
let i++
done
echo "程序结束"
示例2
为指定用户发送在线消息
若用户不在线(未登录系统),则每10分钟试一次,直至用户登录系统后再发送信息
用户名与消息通过位置参数传递给脚本
#!/bin/bash
#检查用户是否在线
name=$1
msg=$2
tty=`who | grep -w $name | awk '{print $2}'`
until [ ! -z $tty ]
do
# echo "检查 $name 是否在线"
sleep 5
tty=` who | grep -w $name | awk '{print $2}'`
done
echo $msg | write $name $tty
#echo "消息已发送,程序结束"
四、Shell函数
- 将命令序列按格式写在一起
- 可方便重复使用命令序列
1.函数的定义方法
方法1:
function 函数名 {
command
}
方法2:
函数名() {
command
}
- 函数定义完之后并不会自动执行,需要调用才行
- 好处在于可以写一段功能代码作为函数,有需要就直接调用定义的时候哪怕出现语法错误也没关系,不调用就不会报错
- 当然我们写函数最终目的还是为了调用,为了实现某个功能块。
函数的调用
直接在脚本里定义函数的代码块后写函数名即可完成调用。
函数名 [参数1] [参数2]
#!/bin/bash
function fun1 {
echo "this is a function!"
}
fun1
2.函数的参数
参数的用法
函数名称 参数1 参数2 参数3 ......
参数的表示方法
$1 $2 $3 … ${10} ${11} …
Shell函数
定义和调用函数、传递参数
3.函数返回值
return表示退出函数并返回一个退出值,脚本中可以用$? 变量显示该值使用原则
- 函数一结束就取返回值,因为$?变量只返回执行的最后一条命令的退出状态码
- 退出状态码必须是0~255,超出时值将为256取余
案例:返回用户输入的数字的两倍
#!/bin/bash
function
test1 {
read -p "请输入一个数字:" num
return $[$num*2]
}
test1
echo $?
函数传参
两个数求和
#!/bin/bash
sum(){
read -p "请输入第一个数:" NUM1
read -p "请输入第二个数:" NUM2
echo "你输入的两个数为: $NUM1 和$NUM2 "
SUM=$(( NUM1 + NUM2 ))
echo "两个数的和为: $SUM"
}
sum
通过脚本传递参数给函数中的位置参数
#!/bin/bash
add (){
let sum=$1+$2
echo $sum
}
add $1 $2 # 调用脚本时传递参数
add 20 30
函数的递归
#!/bin/bash
fa () {
if [ $1 -eq 1 ]
then
echo 1
else
local tp=$[$1 - 1]
local res=$(fa $tp)
echo $[$1 * $res]
fi
}
read -p "请输入:" num
res=$(fa $num)
echo $res
五.数组的定义
数组的定义方法
方法1:
num=(11 22 33 44)
方法2:
num=([0]=55 [1]=66 [2]=77 [4]=88)
方法3:
list="11 12 13 14"
num=($list)
方法4:
数组名[0]="value"
数组名[1]="value"
数组名[2]="value"
获取数组的长度
arr_length=${#arr_number[*]}
获取数据列表
echo ${arr_number[*]}
echo ${arr_number[@]}
数组元素遍历
#!/bin/bash
arr=(1 2 3 4 5 6)
for i in ${arr[*]}
do
echo $i
done
数组(元素)替换
arr=(1 2 3 4 5)
echo ${arr[@]/3/55} # 替换元素3为55
echo ${arr[@]}
arr=(${arr[@]/3/55}) # 实现改变原有数组,如果注释掉原数组不变。
echo ${arr[@]}
数组删除
arr=(1 2 3 45)
unset arr # 删除数组
echo ${arr[*]}
arr=(1 2 3 4 5)
unset arr[4] # 删除第四个元素
echo ${arr[*]}
数组排序算法:冒泡排序
冒泡排序的基本思想是对比相邻的两个元素值,如果满足条件就交换元素值,把较小的元素移动到数组前面,把大的元素移动到数组后面(也就是交换两个元素的位置),这样较小的元素就像气泡一样从底部上升到顶部。
算法思路
#!/bin/bash
array=(90 70 60 40 50 30)
echo "old_array:${array[*]}"
lt=${#array[*]}
# 定义比较轮数,比较轮数为数组长度减1,从1开始
for ((i=1; i<$lt; i++))
do
# 确定比较元素的位置,比较相邻两个元素,较大的数往后放,比较次数随比较轮数而减少
for ((j=0; j<$lt-i; j++))
do
# 定义第一个元素的值
first=${array[$j]}
# 定义第二个元素的值
k=$[$j+1]
second=${array[$k]}
# 如果第一个元素比第二个元素大就互换
if [ $first -gt $second ]
then
# 把第一个元素值保存到临时变量中
temp=$first
# 把第二个元素值赋给第一个元素
array[$j]=$second
# 把临时变量(也就是第一个元素原值)赋给第二个元素
array[$k]=$temp
fi
done
done
# 输出排序后的数组
echo "new_array:${array[*]}"
输入数组元素并排序
#!/bin/bash
# 存入元素
k=0
while true
do
read -p "是否继续存入元素:" doing
if [ $doing == "no" ];then
break
fi
read -p "请输入第$[$k+1]个元素:" key
score[$k]=$key
let k++
done
# 外层为轮
for ((i=1;i<${#score[*]};i++));do
# 内层为次
for ((j=0;j<${#score[*]}-i ;j++));do
# 两数交换
if [ ${score[$j]} -gt ${score[`expr $j + 1`]} ];then
tmp=${score[`expr $j + 1`]}
score[`expr $j + 1`]=${score[$j]}
score[$j]=$tmp
fi
done
done
echo ${score[*]}
ray:${array[*]}"
[外链图片转存中...(img-DSf2up1f-1723197200361)]
### 6.8 输入数组元素并排序
```bash
#!/bin/bash
# 存入元素
k=0
while true
do
read -p "是否继续存入元素:" doing
if [ $doing == "no" ];then
break
fi
read -p "请输入第$[$k+1]个元素:" key
score[$k]=$key
let k++
done
# 外层为轮
for ((i=1;i<${#score[*]};i++));do
# 内层为次
for ((j=0;j<${#score[*]}-i ;j++));do
# 两数交换
if [ ${score[$j]} -gt ${score[`expr $j + 1`]} ];then
tmp=${score[`expr $j + 1`]}
score[`expr $j + 1`]=${score[$j]}
score[$j]=$tmp
fi
done
done
echo ${score[*]}
#!/bin/bash
# 存入元素
k=0
while true
do
read -p "是否继续存入元素:" doing
if [ $doing == "no" ];then
break
fi
read -p "请输入第$[$k+1]个元素:" key
score[$k]=$key
let k++
done
# 外层为轮
for ((i=1;i<${#score[*]};i++));do
# 内层为次
for ((j=0;j<${#score[*]}-i ;j++));do
# 两数交换
if [ ${score[$j]} -gt ${score[`expr $j + 1`]} ];then
tmp=${score[`expr $j + 1`]}
score[`expr $j + 1`]=${score[$j]}
score[$j]=$tmp
fi
done
done
echo ${score[*]}
~~~
