Shell编程--循环结构(for、while、until、shift、continue、break、exit)

循环结构

  • [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的区别就是少了最后一句【脚本结束循环】,直接强行退出当前脚本了

相关推荐
日记跟新中43 分钟前
Ubuntu20.04 修改root密码
linux·运维·服务器
码农君莫笑1 小时前
信管通低代码信息管理系统应用平台
linux·数据库·windows·低代码·c#·.net·visual studio
BUG 4041 小时前
Linux——Shell
linux·运维·服务器
大霞上仙1 小时前
Linux 多命令执行
linux·运维·服务器
晨欣1 小时前
Kibana:LINUX_X86_64 和 DEB_X86_64两种可选下载方式的区别
linux·运维·服务器
AI青年志2 小时前
【服务器】linux服务器管理员查看用户使用内存情况
linux·运维·服务器
dessler2 小时前
Docker-run命令详细讲解
linux·运维·后端·docker
PyAIGCMaster3 小时前
ubuntu装P104驱动
linux·运维·ubuntu
奈何不吃鱼3 小时前
【Linux】ubuntu依赖安装的各种问题汇总
linux·运维·服务器
icy、泡芙3 小时前
T527-----音频调试
linux·驱动开发·音视频