文章目录
前言
在实际工作中,经常会遇到某项任务需要多次执行的情况,而每次执行时仅仅是处理的 对象不一样,其他命令相同。例如,根据通讯录中的姓名列表创建系统账号,根据服务器清 单检查各主机的存活状态,根据 IP 地址黑名单设置拒绝访问的防火墙策略等。
当面对各种列表重复任务时,使用简单的 if 语句已经难以满足要求,而顺序编写全部代码更是显得异常烦琐、困难重重。而在不同场景使用不同的循环语句,可以很好地解决类似的问题。
一、for循环语句
1.for语句的结构
- 读取不同的变量值,用来逐个执行同一组命令
#格式:
for 变量名 in 取值列表
do
命令序列
done
1. 例:
[root@c7-1 ~]# vim for.sh
#!/bin/bash
for i in {a..d} //变量i={abcd}
do //命令序列
echo $i //输出abcd
done //结束循环
[root@c7-1 ~]# bash for.sh //运行脚本
a
b
c
d
[root@c7-1 ~]# vim for1.sh
#!/bin/bash
a=10 //定义变量
for i in `seq $a` //变量i=取值 1~$a
do //命令序列
echo $i hello word //输出
done //结束循环
[root@c7-1 ~]# bash for1.sh
1 hello word
2 hello word
3 hello word
4 hello word
5 hello word
6 hello word
7 hello word
8 hello word
9 hello word
10 hello word
[root@c7-1 ~]# vim ip.sh
#!/bin/bash
for i in {1..255} //定义变量,取值1~255
do //命令序列
ping -c 2 -i 0.1 -W 1 192.168.3.$i &>/dev/null //ping 192.168.3.1 ~ 255 两次,0.1s,1s超时,并输入给黑洞
if [ $? -eq 0 ] //判断上一个命令是否正常
then //条件成立
echo "192.168.3.$i Host $IP is up"
else //以上不成立
echo " 192.168.3.$i Host is down"
fi //结束判断
done //结束循环
################################################################
[root@c7-1 ~]# vim pass.sh
#!/bin/bash
for i in {1..5} //定义变量i,取值1~5
do //命令序列
useradd dsj$i //创建dsj 1~5
echo "12345" | passwd --stdin dsj$i //并设置密码
done //循环结束
[root@c7-1 ~]# bash pass.sh
更改用户 dsj1 的密码 。
passwd:所有的身份验证令牌已经成功更新。
更改用户 dsj2 的密码 。
passwd:所有的身份验证令牌已经成功更新。
更改用户 dsj3 的密码 。
passwd:所有的身份验证令牌已经成功更新。
更改用户 dsj4 的密码 。
passwd:所有的身份验证令牌已经成功更新。
更改用户 dsj5 的密码 。
passwd:所有的身份验证令牌已经成功更新。
[root@c7-1 ~]# vim 99.sh
#!/bin/bash
for ((i=1;i<=9;i++)) //外循环,$i的取值为1,每次循环+1,等于9结束
do
for ((j=1;j<=i;j++)) //内循环,$j的取值为1,每次循环+1,等于$i结束
do
echo -n " $i x $j=$(expr $i \* $j)" //每次循环不换行输出 $ix$j
if [$j -eq $i ] //判断$j和$i是否相等
then //条件成立
echo -e "\n" //则换行
fi //判断结束
done //内循环结束
done //外循环结束
二、while循环语句
1.while语句结构
- 重复测试某个条件,只要条件成立则反复执行
#格式:
while 条件测试操作
do
命令序列
done
2.例:
2.1
[root@c7-1 home]# vim while1.sh
#!/bin/bash
i=1 //定义变量i=1
while [ $i -le 5 ] //当i等于5时停止循环
do
echo $i //每次循环输出i的值
let i++ //每次循环+1
done //结束循环
echo "最后i的值为:$i" //输出最后$i的值
[root@c7-1 home]# bash while1.sh
1
2
3
4
5
最后i的值为:6
[root@c7-1 home]# vim while2.sh
#!/bin/bash
i=1 //定义变量i=1
while [ $i -le 100 ] //重复测试$1是否小于等于100,直至$i等于100
do //命令列表
if [[ $i%3 -ne 0 ]] //条件检测 $i取余3,是否不等于0
then //条件成立
echo "$i" //输出 $i的值
fi //结束判断
let i++ //每次循环i+1
done //结束循环
- 监控httpd服务状态,如果不在启动则提示报错信息
[root@c7-1 home]# vim while3.sh
#1/bin/bash
while ps aux | grep httpd | grep -v grep &>/dev/null //条件检测,查找有没有httpd服务,并输入给黑洞
do
echo "httpd true" //如果有则打印httpd true
sleep 60 //间隔60s
done //结束循环
DATE=`date +%T` //定义变量
echo "服务状态异常"
echo " $DATE httpd flase服务器不在启动,请尽快处理故障 " > /home/httpd.txt //打印什么时间服务报错,并输出给http.txt
[root@c7-1 home]# bash while3.sh
服务状态异常
[root@c7-1 home]# cat /home/httpd.txt
18:47:53 httpd flase服务器不在启动,请尽快处理故障
- 猜数字,猜不对一直猜
[root@c7-1 home]# vim while4.sh
#!/in/bash
NUM=8
while true //条件检测,true为真
do
read -p "请输入数字:" shu //定义交互式变量
if [ $shu -eq $NUM ] //条件判断,$shu 是否等于$NUM 8
then //条件成立则
echo "恭喜你个大聪明猜对了" //打印
break //跳出循环
elif [ $shu -gt $NUM ] //条件判断2,$shu是否大于$NUM
then //条件成立
echo "你猜大了,求你了,猜准点吧" //打印
elif [ $shu -le $NUM ] //条件判断3,$shu是否小于$NUM
then //条件成立
echo "你猜小了,你咋这么笨呢" //打印
fi //判断结束
done //循环结束
#测试
[root@c7-1 home]# bash while4.sh
请输入数字:2
你猜小了,你咋这么笨呢
请输入数字:9
你猜大了,求你了,猜准点吧
请输入数字:8
恭喜你个大聪明猜对了
- 猜商品价格
[root@c7-1 home]# vim while5.sh
#!/bin/bash
PRICE=`expr $RANDOM % 1000` //定义变量,运算一个随机数并取余
a=0 //定义变量a=0
echo "商品实际价格范围0~999,猜猜看是多少" //打印
while true //条件检测,true为真
do //命令序列
read -p "请输入你猜测的价格是多少:" n //定义交互式变量
let a++ //每次循环a+1
if [ $n -eq $PRICE ] //条件检测 $n是否等于随机数
then //条件成立则
echo "恭喜你个大聪明猜对了,实际价格是$PRICE" //打印
echo "你总共猜了$a次" //打印总共猜了$a次
break //终止循环,并退出
elif [ $n -gt $PRICE ] //条件测试 $n 是否大于 随机数
then //条件成立
echo "你猜高了" //打印
elif [ $n -lt $PRICE ] //条件测试 $n 是否小于随机数
then //条件成立则
echo "你猜低了"
fi //结束判断
done //结束循环
三、until语句
1.until语句的结构
- 重复测试某个条件,只要条件不成立则反复执行
until 条件测试操作
do
命令序列
done
2.例:
[root@c7-1 home]# vim 50.sh
#!/bin/bash
i=1 //定义变量i=1
sum=0
until [ $i -eq 51 ] //$i等于51时停止执行
do
sum=$[$i+$sum] //定义sum等于$i + $sum
let i++ //每次循环i+1
done
echo "$sum //打印结果 $sum
[root@c7-1 home]# bash 50.sh
1275
四、函数
- 将命令序列按格式写在一起
- 可方便重复使用命令序列
- shell函数定义 (两种方式):
#格式一:
[ function ] 函数名(){
命令序列
[return x] //使用return或exit可以显示地结束函数
}
#格式二:
函数名 (){ //最常用,因为最简洁
命令序列
}
函数定义完之后并不会自动执行,需要调用才行,好处在于可以写一段功能代码作为函数,有需要就直接调用定义的时候哪怕出现语法错误也没关系,不调用就不会报错
当然我们写函数最终目的还是为了调用,为了实现某个功能块
- 函数返回值:
return表示退出函数并返回一个退出值,脚本中可以用$?变量显示该值
使用原则
- 函数一结束就取返回值,因为$?变量只返回执行的最后一条命令的退出状态码
- 退出状态码必须是0~255,超出时值将为256取余
1.函数的调用
直接再脚本里定义函数的代码块后写函数名即可完成调用
#这是规范的写法
#!/bin/bash
function fun1 { //定义了一个函数叫做fun1
echo "this is a function"
}
#这是简洁的写法
#!/bin/bash
f1 () {
echo hello
}
f2 () {
echo world
}
f3 () {
echo "`f1` `f2`"
}
f3
2.例:
- 部署本地yum源
[root@c7-1 home]# vim dome1.sh
#!/bin/bash
backuprepo (){ //函数名
cd /etc/yum.repos.d
mkdir repo,bak
mv *.repo repo.bak
mount /dev/sr0 /mnt &>/dev/null
}
makelocalrepo (){
echo
[local]
name=local
baseurl=file:///mnt
enable=1
gpgcheck=0 > local.repo
}
userlocalrepo (){
yum clean all > /dev/null
yum makecache > /dev/null
yum install -y httpd > /dev/null
}
backuprepo //调用
makelocalrepo //调用
userlocalrepo //调用
3.函数的作用范围
在 Shell 脚本中函数的执行并不会开启一个新的子Shell,而是仅在当前定义的Shell 环境中有效。
如果Shell脚本中的变量没有经过特殊设定,默认在整个脚本中都是有效的。
在编写脚本时,有时需要将变量的值限定在函数内部,可以通过内置命令local来实现。
函数内部变量的使用,可以避免函数内外同时出现同名变量对脚本结果的影响。
shell脚本中变量默认全局有效
local命令:将变量限定在函数内部使用
[root@c7-1 home]# vim dome4.sh
#!/bin/bash
myfunc () {
local a
a=8
echo $a
}
a=9
myfunc
echo $a
[root@c7-1 home]# bash dome4.sh
8
9
#上述脚本中myfun函数内部使用了local命令设置变量a,其作用是将变量a限定在函数内部。
#myfun函数外部同样定义了变量i,内部变量a和全局变量i互不影响。
#脚本执行时先调用了函数myfun,函数内部变量a为8,所以输出结果是8。
#调用完函数之后,给变量a赋值为9,再打印外部变量i,所以又输出9
- 阶乘
[root@c7-1 home]# vim dome5.sh
#!/bin/bash
fa () { //定义函数名字fa
if [ $1 -eq 1 ] //判断$1(第一个位置)是否等于1
then //为真则
echo 1 //打印 1
else //条件不满足,为假则
local tp=$[ $1 - 1 ] //限定在函数内部使用
local res=$(fa $tp)
echo $[ $1 * $res ] //打印$1x$res
fi //结束判断
}
read -p "请输入:" n
res=$(fa $n) //调用函数fa
echo $res //打印结果
[root@c7-1 home]# bash dome5.sh
请输入:5
120
#上述脚本中先运行交互式变量,$n,根据你输入的数字调用函数fa,
#先if判断我输入的数是否等于1,我输入的5,不等于1,条件为假,运行else
#然后定义tp变量 $1 -1 也就是 5-1 得出4 ;定义res变量,在把tp的值 4 调用给fa;他俩都是内部变量
#然后echo $1 也就是5,乘 $res 4;只有自上而下运行完所有命令 才会再次用$res的调用tp,继续判断$tp是否等于1,直至5-1=4 4-1=3 3-1=2 2-1=1;
#所以5x4x3x2x1=120
五、数组
1.数组的定义
数组是存放相同类型数据的集合,在内存中开辟了连续的空间,通常配合循环使用
2.数组的定义方式
A="1 2 3 456" //定义字符串
A=(1 2 3 4 567) //定义数组
0 1 2 3 4 //其下标示
2.1 第一种方式
直接把要加入数组的元素用小括号括起来,中间用空格分开
数组名=(value0 value1 value2)
num=(11 22 33 44)
${#num} //显示字符串长度
[root@c7-1 home]# echo ${num[*]}
11 22 33 44
2.2 第二种方式
精确的给每一个下标索引定义一个值加入数组,索引数字可以不连续
数组名=([0]=value [1]=value [2]value ...)
num=([0]=55 [1]=66 [2]77 [4]88)
[root@c7-1 home]# echo ${num[*]}
[0]55 [1]66 [2]77 [3]88
2.3 第三种方式
先把要加入数组的元素全部先赋值给一个变量,然后引用这个变量加入到数组
列表名="value0 value1 value2...."
数组名=($列表名)
list="11 12 13 14"
num=($list)
2.4 第四种方式
根据下标定义
数组名[0]="11"
数组名[0]="22"
数组名[0]="33"
数组名[0]="value"
数组名[0]="value"
数组名[0]="value"
2.5 元素切片
[root@c7-1 home]# arr=(1 2 3 4 5 6 7 8 9) //定义数组
[root@c7-1 home]# echo ${arr[*]:2:3} //切片只读取2后面的3个数
3 4 5
2.6 元素替换
[root@c7-1 home]# echo ${arr[*]/4/90} //把4修改成90
1 2 3 90 5 6 7 8 9
2.6 元素删除
[root@c7-1 home]# unset arrt[4] //删除是以下标来删除
[root@c7-1 home]# echo ${arr[*]}
1 2 3 4 5 6 7 8 9
六、冒泡排序
[root@c7-1 home]# vim domem.sh
#确定比较元素的位置,比较相邻的两个元素,较大的数往后放,小的往前放,并且每轮比较次数要随着轮>
数递减
for ((j=0;j<$lt-i;j++))
do
#定义第一个元素的值
first=${array[$j]}
#定义第二个元素的值
k=$[$j+1]
second=${array[$k]}
#如果第一个元素比第二个元素大就互换
if [ $first -gt $second ]
then
#把第一个元素值保存到临时变量里面
tmp=$first
#把第二个元素值保存到第一个元素
array[$j]=$second
#把第三个元素值保存到第二个元素
array[$k]=$tmp
fi
done
done
echo "new_array:${array[*]}"
[root@c7-1 home]# bash domem.sh
old_array:90 70 60 40 50 30
new_array:30 40 50 60 70 90
总结
for语句的结构
while语句的结构
untile语句的结构
Shell函数定义方法
数组使用方法