循环结构
- [1. for语句](#1. for语句)
- [2. while语句](#2. while语句)
- [3. until语句](#3. until语句)
- [4. 循环控制](#4. 循环控制)
-
- [4.1. shift命令](#4.1. shift命令)
- [4.2. continue、break、exit命令](#4.2. continue、break、exit命令)
1. for语句
格式 :
for i in {取值范围} --for是关键字 i是变量名 in是关键字
do --循环体的开始
#循环体
done --循环体的结束
c
[root@localhost script]# vim for.sh
#!/usr/bin/env bash
#
# Author:
# Date: 2024/**/**
for i in {1..100}
do
echo $i
done
[root@localhost script]# vim for1.sh
#!/bin/bash
for (( i=1;i <= 100;i++ ))
do
echo "$i"
done
[root@localhost script]# chmod +x for1.sh
[root@localhost script]# ./for1.sh
//参数解释:
初始值 i=1
条件 i<=100 最大执行100次
增幅 i++ 执行一次 加一
注意:i的值只能是数字,abcd会识别不了的
该方式为计算机中C语言的编程方式
for循环只循环{大括号}内的值
拓展:i++和++i的区别
i++ :先赋值在运算
++i :先运算在赋值
c
//测试生产环境的主机存活性,将up的ip保存在一个文件中,将down的ip保存在一个文件中
[root@localhost script]# vim ip.sh
#!/usr/bin/env bash
# Author:
src_ip="192.168.17"
for i in {2..254}
do
{
ping -c1 $src_ip.$i &>/dev/null
if [ $? -eq 0 ];then
echo "alive: $src_ip.$i" >> ip_up.txt
echo "alive: $src_ip.$i"
else
echo "down: $src_ip.$i" >> ip_down.txt
echo "down: $src_ip.$i"
fi
} & # &就是放入后台执行
done
wait
echo "finish..."
//参数详解:wait:等待上面命令后台执行结束后(即上一个的进程终止),在执行下面的echo命令
[root@localhost script]# chmod +x ip.sh
[root@localhost script]# ./ip.sh
c
//for循环+seq的方式批量创建用户
[root@localhost script]# vim user.sh
#!/usr/bin/bash
read -p "请设置用户名、数量(默认不输入则为1)、密码(默认不配置则为123456):" prefix num pass
if [[ -z $prefix ]];then #如果用户名为空,则:
echo "请输入正确的用户名"
else #用户名不为空时,则:
cat <<EOF
用户前缀:$prefix
用户数量:${num:-1}
用户密码:${pass:-123456}
EOF
for i in $(seq 1 ${num:-1})
#seq 1 $num $num就是创建用户的数量;-1:变量置换,当用户没有输入$num的时候,则默认为1
do
user=$prefix$i
id $user &> /dev/null
if [[ $? -eq 0 ]];then
echo "$user is already exist!"
exit 0
else
useradd $user &> /dev/null
echo ${pass:-123456} | passwd --stdin $user &>/dev/null #-123456:变量置换,当用户没有输入密码的时候,则使用默认密码123456
fi
done
echo "starting create users..."
fi
//执行脚本,并且不设置密码
[root@localhost ~]# bash user.sh
请设置用户名、数量(默认不输入则为1)、密码(默认不配置则为12456):tom 2
用户前缀:tom
用户数量:2
用户密码:123456
//或者将if判断直接注释掉,使用下面的操作一步到位
id $user &> /dev/null || (useradd $user &> /dev/null && echo ${pass:-123456} | passwd --stdin $user &>/dev/null)
//逻辑或,判断用户是否存在,如果执行失败,则说明不成功,那就创建用户,设置密码;如果用户存在,则后面的不执行
c
//参数详解:seq 打印序列号,只跟数字,用于产生从某个数到另外一个数之间的所有整数。
[root@localhost ~]# seq 10
1
2
3
4
5
6
7
8
9
10
//打印奇数:seq 1 2 10 表示1到10以内的奇数,2是步长,因为中间隔2
[root@localhost ~]# seq 1 2 10
1
3
5
7
9
seq -w $num:-w 所有数取最宽的宽度,前面没有的自动补零。
为什么要用seq呢?{1...100}不是也可以使用么?seq有更方便么?
主要是因为在for循环中,循环100次,就得写{1...100};循环10000次,那就得写成{1...10000};每次都要修改太麻烦了;此时我们可以设置一个变量,例如a=10000,然后使用seq $a 就可以循环10000次了;
c
#例子:
a=100
for i in $(seq $a)
do
echo $i
done
for循环之通过用户文件批量创建用户
//将用户全部都写到一个文件中
[root@localhost ~]# cat user.txt
zhangsan
lisi
wangwu
zhaoliu
sunqi
qianba
//通过脚本来创建用户并指定用户密码(用户名和密码都是一样的)
[root@localhost ~]# cat cat_file.sh
#!/bin/bash
for i in `cat /root/user.txt`;do
id $i &>/dev/null || (useradd $i && echo $i|passwd --stdin $i &>/dev/null) #逻辑或,前面执行失败则执行后面的
done
[root@localhost ~]# id zhangsan
uid=1008(zhangsan) gid=1008(zhangsan) 组=1008(zhangsan)
//批量删除
[root@localhost ~]# for i in $(cat /root/user.txt);do userdel -r $i;done
2. while语句
格式 :
while 条件 --while关键字,条件和if的条件一样,为真时执行
--while循环当条件为真的时候循环同时会一直循环,也就所说的死循环,为假时不循环
do --循环体的开始
#循环体
done --循环体的结束
注意:while循环处理文件里面的行比较擅长,不管有没有空格都是一行。
c
[root@localhost ~]# vim c.sh
#!/usr/bin/bash
i=1
while [ $i -lt 50 ]
do
echo $i
done
//注意观察,请问如何能够自动终止?
在echo $i前面加一行, i=$((i+1)) 让它运行完一次做一次+1的运算
#!/usr/bin/bash
i=1
while [ $i -lt 50 ]
do
#i=$((i+1))
echo $i
i=$((i+1))
done
c
变量运算回顾:
[root@localhost ~]# a=1
[root@localhost ~]# b=1
[root@localhost ~]# echo $a+$b //直接打印是无法进行运算的
1+1
[root@localhost ~]# echo $((a+b)) //需要用2个小括号或者用一个中括号替代[]
2
案例一:通过一个文件批量创建用户
背景:写一个脚本,满足以下需求及应用,如一个文件的内容如下,根据文件内容实现批量创建用户,第一列为用户名,第二列为密码
c
[root@localhost]# vim user_pass.txt //创建用户和密码文件
user1 jiangli123
user2 jiangli456
user3 jiangli567
user4 jiangli789
user5 jiangli012
[root@localhost]# vim create_user.sh //编写脚本
#!/usr/bin/bash
[ $UID -ne 0 ] && exit 1 #判断是否为root用户
while read line #读入,从/root/user_pass.txt中读入
do
user=$(echo $line | awk '{print $1}') #用户
pass=`echo $line | awk '{print $2}'` #密码
id $user &> /dev/null || useradd $user && echo $pass | passwd $user --stdin
done < /root/user_pass.txt
[root@localhost script]# chmod +x create_user.sh
[root@localhost script]# bash create_user.sh
c
//批量删除用户脚本:
[root@localhost]# vim del_user.sh #编写脚本
#!/usr/bin/bash
[ $UID -ne 0 ] && exit 1 #判断是否为root用户
while read line #读入,从/root/user_pass.txt中读入
do
user=`echo $line | awk '{print $1}'` #用户
id $user &> /dev/null && userdel -r $user
done < /root/user_pass.txt
案例二:
c
[root@localhost script]# vim while.sh
#!/usr/bin/env bash
#
# Author:
while true #条件判断,为真时,永远都为真,所以是死循环
#clear
do
cat <<-EOF
+-------------------------------------------------------------------------+
| System_tools V1.0 |
+-------------------------------------------------------------------------+
| a. Stop And Disabled Firewalld. |
| b. Disabled SELinux Secure System. |
| c. Install Apache Service. |
| d. Quit |
+-------------------------------------------------------------------------+
EOF
echo " Please input your select: " && read var
case "$var" in
"a")
systemctl stop firewalld && systemctl disable firewalld
clear
;;
"b")
sed -ri s/SELINUX=enforcing/SELINUX=disabled/g /etc/selinux/config
clear
;;
"c")
yum -y install httpd httpd-tools
clear
;;
"d")
exit
;;
*)
echo "请按照上方提供的选项输入!!!"
;;
esac
if [ $? -ne 0 ];then
echo "Warning: Your program exist ERROR!!!"
break
fi
done
[root@localhost script]# chmod +x while.sh
[root@localhost script]# ./while.sh
案例三:循环嵌套(了解)
c
[root@localhost script]# vim test4.sh
#!/usr/bin/bash
for i in {1..3} #总共循环三次
do
while [ $i -lt 10 ] #当$i小于10时,就会执行do循环体里面的操作
do
echo $i
let i++
done
done
[root@localhost ]# bash test4.sh //最终运行出来的效果
1
2
3
4
5
6
7
8
9
//可以将上面看成第一次for循环执行的结果
2
3
4
5
6
7
8
9
//可以将上面看成第二次for循环执行的结果
3
4
5
6
7
8
9
//可以将上面看成第三次for循环执行的结果
拓展知识:let i++
是shell中内置的用于执行算术运算的工具
c
[root@localhost ~]# i=1
[root@localhost ~]# let i++
[root@localhost ~]# echo $i
2
[root@localhost ~]# let i++
[root@localhost ~]# echo $i
3
[root@localhost ~]# let i++
[root@localhost ~]# echo $i
4
//等同于
[root@localhost ~]# i=2
[root@localhost ~]# i=$((i+3))
[root@localhost ~]# echo $i
5
//或者可以将$(())替换成[]
[root@localhost ~]# i=$[i+3]
[root@localhost ~]# echo $i
8
//例:
[root@localhost ~]# a=2
[root@localhost ~]# b=3
[root@localhost ~]# let ab=a*b
[root@localhost ~]# echo $ab
6
//练习:批量创建用户
[root@localhost ~]# cat adduser.sh
#!/bin/bash
for i in {1..10}
do
useradd jli2302$i
done
[root@localhost ~]# bash adduser.sh
[root@localhost ~]# id jli23021
uid=1017(jli23021) gid=1017(jli23021) 组=1017(jli23021)
[root@localhost ~]# id jli230210
uid=1026(jli230210) gid=1026(jli230210) 组=1026(jli230210)
//建立批量删除用户脚本
[root@localhost ~]# vim deluser.sh
#!/usr/bin/bash
read -p "请输入用户名: " name
read -p "请输入要删除的个数? " num
read -p "确认要删除$name[Y|y]: " del
if [ $del = Y ] || [ $del = y ];then
for i in $(seq $num )
do
user=$name"$i"
(id $user &>/dev/null && userdel -r $user) || (id $user &>/dev/null || echo "用户不存在")
#判断用户是否存在,存在则删除;不存在则打印用户不存在
done
fi
//运行脚本
[root@localhost ~]# chmod +x deluser.sh
[root@localhost ~]# ./deluser.sh
//编写99乘法表(了解)
[root@localhost ~]# cat 99.sh
#!/bin/bash
for i in {1..9}; do
for j in {1..9}; do
if [ $j -le $i ]; then #-le:小于等于
let "result=i*j"
#或者:result=$[i*j]
echo -n "$i*$j=$result " #注意后面有空格
fi
done
echo "" #打印空行
done
//let "result=i*j":将 $i 和 $j 相乘的结果赋值给变量 $result。
//echo -n "$i*$j=$result " 命令的作用是将当前的乘法式子和结果输出到同一行上,并以空格作为分隔符
//运行结果:
[root@localhost ~]# bash 99.sh
1*1=1
2*1=2 2*2=4
3*1=3 3*2=6 3*3=9
4*1=4 4*2=8 4*3=12 4*4=16
5*1=5 5*2=10 5*3=15 5*4=20 5*5=25
6*1=6 6*2=12 6*3=18 6*4=24 6*5=30 6*6=36
7*1=7 7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=49
8*1=8 8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=64
9*1=9 9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81
3. until语句
格式 :
until 条件 --当后面的条件表达式为假的时候的才循环,为真的时候就停止了;跟while循环是相反的
do --循环体的开始
#循环体
done --循环体的结束
c
[root@localhost script]# cat until.sh
#!/bin/bash
x=1
until [ $x -ge 10 ] #-ge:大于等于;1<10,为假,为假那就执行
do
echo $x
x=`expr $x + 1`
done
//while:为真时执行,所以一次都不会执行,因为1永远不会大于10;要让它跟until效果一样的话,就将ge改成lt小于
//相当于:
x=1
while [ ! $x -ge 10 ]
do
echo $x
x=`expr $x + 1`
done
//注释:
-ge 大于等于
-lt 小于
1 >= 10 为假 #执行until循环
! 1 <= 10 为真 #不会执行until循环
! 1 -le 10 为假 #会执行until循环
[root@localhost script]# chmod +x until.sh
[root@localhost script]# ./until.sh
1
2
3
4
5
6
7
8
9
//expr回顾:expr命令可以实现数值运算、数值或字符串比较、字符串匹配、字符串提取、字符串长度计算等功能
[root@localhost ~]# a=1
[root@localhost ~]# expr $a + 1 //运算符两边记得空格
2
4. 循环控制
4.1. shift命令
位置参数可以用shift命令左移。比如shift 3
表示原来的$4
现在变成$1
,原来的$5
现在变成$2
等等,原来的$1、$2、$3丢弃,$0不移动。
不带参数的shift命令相当于shift 1
。
对于位置变量或命令行参数,其个数必须是确定的,或者当Shell程序不知道其个数时,可以把所有参数一起赋值给变量 $*。
若用户要求 Shell 在不知道位置变量个数的情况下,还能逐个的把参数一一处理,也就是在 $1 后为 $2,在 $2 后面为 $3 等,则需要用shift把所有参数变成$1
c
//位置变量回顾:
[root@localhost ~]# vim weizhi.sh
#!/bin/bash
echo "第一个位置参数为:$1"
echo "第二个位置参数为:$2"
echo "第三个位置参数为:$3"
echo "第四个位置参数为:$4"
[root@localhost ~]# bash weizhi.sh 1 2 3 hello
第一个位置参数为:1
第二个位置参数为:2
第三个位置参数为:3
第四个位置参数为:hello
//shift 3则是将hello移到第一个位置参数
[root@localhost ~]# vim weizhi.sh
shift 3
#!/bin/bash
echo "第一个位置参数为:$1"
echo "第二个位置参数为:$2"
echo "第三个位置参数为:$3"
echo "第四个位置参数为:$4"
[root@localhost ~]# bash weizhi.sh 1 2 3 hello
第一个位置参数为:hello
第二个位置参数为:
第三个位置参数为:
第四个位置参数为:
[root@localhost ~]# bash weizhi.sh 1 2 3 hello 5 6 7
第一个位置参数为:hello
第二个位置参数为:5
第三个位置参数为:6
第四个位置参数为:7
[root@localhost script]# vim x_shift3.sh
#!/bin/bash
shift
echo "第一个位置参数: $1"
[root@localhost script]# bash x_shift3.sh 2 3
第一个位置参数: 3
4.2. continue、break、exit命令
break
:结束并退出本次循环
continue
:在循环中不执行continue下面的代码,转而进入下一轮循环
exit
:退出脚本;常带一个整数给系统,如 exit 0
可理解为:break是退出当前所有循环;continue是跳过当前条件循环,继续下一轮条件循环;exit是直接退出整个脚本
例如: 在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,Shell使用两个命令来实现该功能:break和continue。
三者记忆法,斗地主:
break:一次性打完手里的牌后,直接结束了游戏,打了别人一个春天(结束了本次循环)
continue:王炸,要不起,过,但是牌局还没结束,还在继续(跳过本次循环,进入下一轮循环)
exit:斗地主软件崩溃,直接退出程序
break命令允许跳出所有循环(终止执行后面的所有循环)。
下面的例子中,脚本进入死循环直至用户输入数字大于5。要跳出这个循环,返回到shell提示符下,需要使用break命令。
c
[root@localhost script]# vim break.sh
#!/bin/bash
while : #默认为真
do
echo -n "Input a number between 1 to 5: "
read aNum
case $aNum in
1|2|3|4|5)
echo "Your number is $aNum!"
;;
*)
echo "You do not select a number between 1 to 5, game is over!"
break
;;
esac
done
//将break注释掉观察结果:会陷入死循环,直接自己手动crtl c结束
break示例:
[root@localhost ~]# vim break2.sh
#!/usr/bin/bash
for i in {1..10}
do
if [ $i -eq 7 ];then
#continue
break #执行到第7次时,退出当前循环,后面的循环体不会再循环了
#exit 34
else
echo $i
fi
echo "本次输出结束"
done
echo "脚本结束循环"
//执行结果:
[root@localhost ~]# bash break2.sh
1
本次输出结束
2
本次输出结束
3
本次输出结束
4
本次输出结束
5
本次输出结束
6
本次输出结束
脚本结束循环
//continue命令与break命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。
continue示例:
[root@localhost ~]# vim continue.sh
#!/usr/bin/bash
for i in {1..10}
do
if [ $i -eq 7 ];then
continue #执行到第7次时,跳过本次循环
#break
#exit 34
else
echo $i
fi
echo "本次输出结束"
done
echo "脚本结束循环"
//执行结果:
[root@localhost]# sh continue.sh
1
本次输出结束
2
本次输出结束
3
本次输出结束
4
本次输出结束
5
本次输出结束
6
本次输出结束
8
本次输出结束
9
本次输出结束
10
本次输出结束
脚本结束循环
break和exit的区别:exit是直接强制退出脚本,循环外面的语句也不会继续执行了;break是退出当前循环,循环外的语句还是会执行的
c
exit示例:
[root@localhost script]# vim exit.sh
#!/usr/bin/bash
for i in {1..10}
do
if [ $i -eq 7 ];then
#continue
#break
exit 34 #执行到第7个的时候,强行退出当前脚本,后续的任何操作都不再执行
else
echo $i
fi
echo "本次输出结束"
done
echo "脚本结束循环"
//运行结果
[root@localhost ~]# bash exit.sh
1
本次输出结束
2
本次输出结束
3
本次输出结束
4
本次输出结束
5
本次输出结束
6
本次输出结束
[root@localhost ~]# echo $?
34 //该状态码为自定义的状态码
可以通过结果看到,跟break的区别就是少了最后一句【脚本结束循环】,直接强行退出当前脚本了