在实际工作中,经常会遇到某项任务需要多次执行的情况,而每次执行时仅仅是处理的对象不一样,其他命令相同。例如,根据通讯录中的姓名列表创建系统账号,根据服务器清单检查各主机的存活状态
当面对各种列表重复任务时,使用简单的if 语句已经难以满足要求,而顺序编写全部代码更是显得异常烦琐、困难重重
for循环语句
一、 for循环
1、语法结构
(1)列表循环
(2)不带列表循环
(3)类c风格的for循环
遍历
for 变量名 in { list}
do
command
done
for i in {a..c}
do
echo $i
done
for i in {1..5} //{ }里面不识别变量
do
echo $i
done
for i in 'seq 5' // seq 5=1、2、3、4、5序列 do echo $i done
a=10 for i in `seq $a` //seq可以引用变量 do echo $i done
for 变量名 in a b c do command done
for i in a b c //这里实际没有调用,所以就相当于in后面几个参数就循环几次 do echo 123 done
for i in a b c //这里调用到i变量了,所以就正常显示i的值(abc) do echo $i done
案列
例1:打印1-5这5个数字
[root@server ~]# vim for.sh
#!/ bin/ bash
for i in {1..5)
do
echo $i
done
例2:打印5次hello world
注意:虽然我们定义了一个变量i,但是没有使用它,它只是控制循环次数
[root@server ~] # vim for.sh
#!/bin/bash
for i in {1..5}
do
echo hello world
done
例3:打印abcde
[root@server ~]# vim for.sh #!/bin/bash for i in a b c d e do echo $i done
例4:输出0-50之间的偶数
[root@server ~]# vim for.sh
#!/ bin/ bash
for i in {0..50..2] //..2代表步长为2,每隔2个
do
echo $i
done
附1小技巧:花括号{}和seq在for循环的应用:
for i in {1..50..2] 1-50的奇数
for i in {2..50..2) 1-50的偶数
for i in {10..1}1-10倒序排列
for i in $(seq 10)1-10正序排列
for i in $(seq 10 -1 1) 1-10倒序排列
for i in $(seq 1 2 10)1-10的奇数,中间为步长
for i in s (seq 0 2 10) 1-10的偶数,中间为步长例子
for i in $ (seq o 2 10) ; do ;echo $i ;done
不带列表循环执行时由用户指定参数和参数的个数决定的
for 变量名 do command done
例1:打印hello第一种:
[ root@server ~]# vim for2.sh #!/bin/ bash for i do echo hello done [root&server ~]# ./for2.sh //没有给脚本传参所以执行了没有结果 [ root@server ~-]# ./for2.sh a //把a赋值给变量i,i有值了它就开始执行do ..done了hello
第二种:
for i do echo $i done [root@client ~] # bash for.sh hello hello
( 3)类c风格的for循环
for ( (expr1;expr2;expr3)) do command done expr1:定义变量并赋初值 expr2:决定是否循环 expr3:决定循环变量如何改变,决定循环什么时候退出
例1:打印1-5迭代
[zoot@serveE~]# vim for3.sh #!/bin/bash for ((i=1;i<=5;i++) )do echo $i done
注: i++ : i=1+1先赋值再运算i=1之后再+1
++i : 1+1=i先运算再赋值1+1之后再=i
例2:打印1-10的奇数
[ root@server ~]# vim for3.sh #! /bin/bash for ( (i=1 ;i<=10;i+=2)) //i=i+2 do echo $i done
附2:类c风格运算符用法
++ 自身变量+1
– 自身变量-1
+=5自身变量+5
-=5自身变量-5
=5 自身变量5
/=5自身变量/5
%=5自身变量%5
例3:计算1-100的奇数和
[rooteserver ~]# vim for3.sh #!/bin/bash sum=0 for ( (i=1 ;i<=100 ;i+=2) ) do let sum=$i+$sum done echo "1-100的奇数和为:$sum"
案例:
例1、批量添加用户两种方式
1)以后缀批量变化添加
for i in {1..5}
do
useradd stu$i
echo "123" | passwd --stdin stu$i
done
2)脚本批量添加用户
#!/bin/ bash ULIST=$ (cat / root/users.txt) for UNAME in $ULIST do useradd $UNAME echo "123456" | passwd --stdin $UNAME &>/dev/null done
//批量删除用户的脚本
vim udelfor.sh #! /bin/bash ULIST=$(cat/root/ users.txt) for UNAME in $ULIST do userdel -r $UNAME &>/ dev/ null done
例2、根据IP地址列表检查主机状态
-c发送包的数量;-i发送ping包间隔;-W超时时间
1)根据文本查看
#!/bin/bash HLIST=$ (cat /root/ ipadds.txt) for IP in $HLIST do ping -c 3 -i 0.2 -W 3 $IP &> l dev/ null if [ $? -eq 0 ];then echo "Host $IP is up." else echo "Host $IP is down . " fi done
2)根据段查看
network="192.168.10"
for addr in { 1..254 }
do
ping -c 2 -i 0.5 -w 3 $network.$addr &> /dev/null
if [ $? -eq 0 ] ;then
echo " $network. $addr is up"
else
echo "$network.$addr is down"
fi
done
#用户输入密码,脚本判断密码是否正确,输入正确提示正确信息,连续输错3次则报警
vim test. sh
#!/bin/bash
init=123456
for i in {1. .3 }
do
read -p "请输入密码:" pass
if [ $pass == $init ] ; then
echo “密码正确"
exit
fi
done
echo“警告:密码错误"
#幸运会员
#!/bin/ bash
a=0
b=0
c=0
for ( (i=1;i<=10;i++) )
do
num=$ (expr $ [RANDOM%3+1])
LIST=$(cat /opt/name.txt | grep "$num" | awk -F: '{print $2}')
case $LIST in
zhangsan)
let a++
; ;
lisi)
letb++
;;
*)
let c++
esac
echo "$LIST"
done
echo "zhangsan : $a次、lisi:$b次、dangwu : $c次"
while循环
1、语法结构
2、死循环
(1)语法结构(3种)
while循环一般用于有条件判断的循环,若判断条件为真,则进入循环,当条件为假就跳出循环
1、语法结构
while 表达式
do
command
done
例1:打印1-5
[root@server ~]# vim while.sh
#!/bin/bash
i=1
while [ $i -le 5 ]
do
echo $i
let i++ //注意这里如果不改变$i的值,会变成死循环
#i=$[$i+1] //两种写法
done
echo "最后i的值为: $i"
例2:输出1-100之间不能被3整除的数字
[root@server myscripts]# vim 3.sh #!/bin/bash i=1 while [ $i -le 100 ] do if [[ $i%3 -ne 0 ]] then echo "$i" fi let i++ done
例3:打印1-100的和
#!/bin/bash
i=1
sum=0
while [ $i -le 100 ]
do
let sum=$i+$sum
let i++
done
echo $sum
例4:监控某服务(httpd)运行状态
while ps aux | grep httpd | grep -v grep &> /dev/null do echo "httpd 正在运行中" sleep 2 done echo "httpd 不在运行"
while死循环
while [ 1 -eq 1 ] //写一个永远为真的表达式,1等于1这个条件永远为真,所以这个脚本会一直循环下去
do
command
done
while true
do
command
done
while :
do
command
done
例1:猜数字,猜不对就一直猜=========
num=10
while true
do
read -p "请输入数字:" shu
if [ $shu -eq $num ];then
echo "你猜对了"
break
elif [ $shu -gt $num ];then
echo "你猜大了"
elif [ $shu -lt $num ];then
echo "你猜小了"
fi
done
例2
1.批量添加规律编号的用户========
#!/bin/bash
USERS="stu"
i=1
while [ $i -le 20 ]
do
useradd ${USERS}$i
echo "123456" | passwd --stdin ${USERS}$i &> /dev/null
let i++
done
例3
猜商品价格游戏
$random用于生成0—32767的随机数
第一种方法
#!/bin/bash PRICE=$(expr $RANDOM % 1000) a=0 echo "商品实际价格范围为 0-999,猜猜看是多少?" while true do read -p "请输入你猜测的价格数目:" n let a++ if [ $n -eq $PRICE ] ; then echo "恭喜你答对了,实际价格是 $PRICE" echo "你总共猜测了 $a 次" exit 0 elif [ $n -gt $PRICE ] ; then echo "你猜高了!" else echo "你猜低了!" fi done
第二种方法
#!/bin/bash sorce=$[$RANDOM % 1000] a=1 num=0 while[ $a -lt 2 ] do read -p "请输入你猜的价格(1-999之 间) :" price if [ $price -eq $sorce ] ; then echo "恭喜你猜对了!" let num++ let a++ elif [ $price -gt $sorce ] ; then echo "你猜高了!" let num++ elif[ $price -lt $sorce ] ; then echo "你猜小了!" let num++ fi done echo "你一共猜了$num次!"
例4 实时监控本机内存和硬盘剩余空间,剩余内存小于500M、根分区剩余空间小于1000M 时,发送报警邮件给 root 管理员
#!/bin/bash
#提取根分区剩余空间
disk_size=$(df / |awk '///{print $4}')
#提取内存剩余空间
mem_size=$(free |awk '/Mem/{print $4}')
while :
do
#注意内存和磁盘提取的空间大小都是以Kb 为单位
if [ $disk_size ‐le 512000 -a $mem_size ‐le 1024000 ];then
mail ‐s Warning root <
例5 定义网卡流量
#!/bin/bash
#定义流量单位
DW=kb/s
while :
do
#定义某一时间点的提取网卡流量数值,我这里的网卡是ens33
OLD_IN=$(cat /proc/net/dev | awk '$1~/ens33/{print $2}')
OLD_OUT=`cat /proc/net/dev | awk '$1~/ens33/{print $10}'`
sleep 5
#定义下一个时间点的提取网卡流量数值。
NEW_IN=$(cat /proc/net/dev | awk '$1~/ens33/{print $2}')
NEW_OUT=`cat /proc/net/dev | awk '$1~/ens33/{print $10}'`
#进行流量的计算,默认是Bytes,转换成kb/s
IN=$[$[$NEW_IN - $OLD_IN]/1024]$DW
OUT=$[$[$NEW_OUT - $OLD_OUT]/1024]$DW
sleep 5
#打印对应的值
echo -e "接收数据:${IN}t发送数据:$OUT"
#!/bin/bash
i=1
sum=0
while [ $i -le 5 ]
do
echo "进入第$i家商店"
read -p "是否进入看看(yes/no)" doing
while [ $doing = "yes" ]
do
echo "1:衣服¥200"
echo "2:鞋子¥150"
echo "3:手套¥40"
echo "4:裤子¥155"
read -p "请选择需要购买的商品序列:" num
case $num in
1)
echo "衣服购买成功"
expr $[sum+=200] &> /dev/null
;;
2)
echo "鞋子购买成功"
expr $[sum+=150] &> /dev/null
;;
3)
echo "手套购买成功"
expr $[sum+=40] &> /dev/null
;;
*)
echo "裤子购买成功"
expr $[sum+=155] &> /dev/null
esac
read -p "是否继续进行购买(yes/no)" doing
done
let i++
if [ $doing = "no" ]
then
continue
fi
done
echo "购物总价:$sum"
until循环
跟while相反,条件为假进入循环,条件为真退出循环
语法结构
until 表达式
do
command
done
例1:计算1-50的和1275两种写法
第一种
#!/bin/bash
i=0;s=0
until [ $i -eq 51 ]
do
let s+=i #s+=i,等价于s=s+i,使用加赋值
let i++
done
echo $s
第二种
vim 50.sh
#!/bin/bash
i=1
sum=0
until [ $i -eq 51 ] //这里注意如果是50的话条件为真循环结束它计算的是1-49的和,until [ $i -gt 50 ]也行
do
sum=$[$i+$sum]
let i++
done
echo "$sum"
死循环结构
until false
do
command
done
until [ 1 -ne 1 ]
do
command
done
案例2、登录zhangsan用户 使用root 发消息个zhangsan ================
#!/bin/bash
username=$1
#判断信息格式
if [ $# -lt 1 ];then
echo "Usage:`basename $0` []"
exit 1
fi
#判断用户是否存在
if grep "^$username:" /etc/passwd >/dev/null ;then :
else
echo "用户不存在"
exit 1
fi
#用户是否在线,如果不在线每5秒联系一次
until who|grep "$username" >/dev/null
do
echo "用户不存在"
sleep 5
done
mes=$*
echo $mes | write $username
注:测试时切换下用户
循环控制语句
for循环一般会搭配条件判断语句和流程控制语句一起执行,那么就会出现需要跳过循环和中止循环的情况,控制循环的命令有以下3个
1、continue
继续,但不会执行循环体内下面的代码了,开始重新开始下一次循环
例1:打印1-5的数字,3不打印
[root@server ~]# vim for4.sh
#!/bin/bash
for ((i=1;i<=5;i++))
do
if [ $i -eq 3 ];then
continue
else
echo $i
fi
done
结果是1245,3不输出,因为跳出后面的echo语句执行下一次循环了
2、break
打断,马上停止本次循环,执行循环体外的代码
例2:1-10的数字,7后面的都不打印
[root@server ~]# vim for4.sh
#!/bin/bash
for ((i=1;i<=10;i++))
do
if [ $i -eq 8 ];then
break
else
echo $i
fi
done
3、exit
直接跳出程序,后面可跟状态返回码如exit 1等等
for i in {1..5}
do
if [ $i -eq 3 ];then
exit 100
else
echo $i
fi
done
echo hi
直接跳出程序所以不会执行最后的echo hi,并且返回码是100通过$?查看
shell函数
函数的作用
函数的定义(两种方式)
第一种
function 函数名 {
command
} //这是一种规范写法
第二种
函数名( ){ //最常用因为最简洁
command
}
函数定义完之后并不会自动执行,需要调用才行,好处在于可以写一段功能代码作为函数,有需要就直接调用
定义的时候哪怕出现语法错误也没关系,不调用就不会报错
当然我们写函数最终目的还是为了调用,为了实现某个功能块
函数返回值:
return表示退出函数并返回一个退出值,脚本中可以用
?
变
量
显
示
该
值
使
用
原
则
:
1
、
函
数
一
结
束
就
取
返
回
值
,
因
为
?变量显示该值 使用原则: 1、函数一结束就取返回值,因为
?变量显示该值使用原则:1、函数一结束就取返回值,因为?变量只返回执行的最后一条命令的退出状态码
2、退出状态码必须是0~255,超出时值将为取余256
例
444 % 256
函数的调用
直接在脚本里定义函数的代码块后写函数名即可完成调用
#!/bin/bash
function fun1 { //定义了一个函数叫做fun1
echo "this is a function!" //函数体的功能是打印"this is a function!
}
fun1 //直接写函数名就会运行函数体内的代码
[root@server myscripts]# . fun.sh
this is a function!
注意①:函数名必须是唯一,如果先定义了一个,再用同样的名称定义,第二个会覆盖第一个的功能,出现了你不想要的结果,所以这里一定要注意不要重名!
#!/bin/bash
f1 (){
echo hello
}
f1 (){
echo world
}
f1
[root@localhost ~]# . f1.sh
world
注意②:调用函数之前必须先进行定义!
#!/bin/bash
f1 (){
echo hello
}
f3 (){
echo "$(f1) $(f2)"
}
f3
f2 (){
echo world
}
[root@localhost ~]# bash f1.sh //因为f3函数里调用了f2,调用了f3的时候并不能知道f2的定义,因为f2在f 运行之后才被定义,所以运行的脚本会报错
f1.sh:行8: f2: 未找到命令
hello
#!/bin/bash
f1 (){
echo hello
}
f3 (){
echo "$(f1) $(f2)" //这里虽然提前调用了f2,但是f2的函数还是在运行f3之前定义的所以他能够找到
}
f2 (){
echo world
}
f3
注意③:不一定要在脚本开头就定义函数,只要调用之前定义就可以
如果是在其他地方嵌套调用等情况,不可以直接写函数的值,避免无法识别,可以用反撇号引起来,相当于是调用函数的结果
#!/bin/bash
f1 (){
echo hello
}
f2 (){
echo world
}
f3 (){
echo "`f1` `f2`" //这里如果直接是f1和f2的话,打印出来的不会是hello world而会是f1 f2
}
f3
总结
编写shell脚本步骤:
1.确定需求
2.知晓命令
3.定义变量
4.完善语法结构



