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

相关推荐
虾..12 小时前
Linux 软硬链接和动静态库
linux·运维·服务器
Evan芙12 小时前
Linux常见的日志服务管理的常见日志服务
linux·运维·服务器
hkhkhkhkh12314 小时前
Linux设备节点基础知识
linux·服务器·驱动开发
牢七14 小时前
Ghghhhnj
dash
HZero.chen15 小时前
Linux字符串处理
linux·string
张童瑶15 小时前
Linux SSH隧道代理转发及多层转发
linux·运维·ssh
汪汪队立大功12315 小时前
什么是SELinux
linux
石小千15 小时前
Linux安装OpenProject
linux·运维
柏木乃一15 小时前
进程(2)进程概念与基本操作
linux·服务器·开发语言·性能优化·shell·进程
Lime-309015 小时前
制作Ubuntu 24.04-GPU服务器测试系统盘
linux·运维·ubuntu