【Linux】循环语句

【Linux】循环语句之for&while

1. 条件测试

1.1 条件测试常用语法

在 bash 编程里,条件测试常用的语法如下:

  • 语法1:test <判断表达式>,test命令和"<判断表达式>"之间至少有一个空格。
  • 语法2:[ <判断表达式> ] ,和test命令的用法相同,这是推荐方法。[]的边界和内容之间至少有一个空格。
  • 语法3:[[ <判断表达式> ]] ,是比test和[]更新的语法格式。[[]]的边界和内容之间至少有一个空格。
  • 语法4:(( <判断表达式> )) ,一般用于 if 语句。(())(双小括号)两端不需要有空格。
  • 语法5:comand,命令的返回值确定表达式的真值或假值。

条件测试常用于判断以下内容:

  • 字符串
  • 数值
  • 文件、进程、服务、用户等

1.2 test帮助信息

bash 复制代码
[harvy@CentOS7 ~ 09:42:41]$ man test
test: test [expr]
    Evaluate conditional expression.

    Exits with a status of 0 (true) or 1 (false) depending on
    the evaluation of EXPR.  Expressions may be unary or binary.  Unary
    expressions are often used to examine the status of a file.  There
    are string operators and numeric comparison operators as well.

    The behavior of test depends on the number of arguments.  Read the
    bash manual page for the complete specification.

    File operators:

      -a FILE        True if file exists.
      -b FILE        True if file is block special.
      -c FILE        True if file is character special.
      -d FILE        True if file is a directory.
      -e FILE        True if file exists.
      -f FILE        True if file exists and is a regular file.
      -g FILE        True if file is set-group-id.
      -h FILE        True if file is a symbolic link.
      -L FILE        True if file is a symbolic link.
      -k FILE        True if file has its `sticky' bit set.
      -p FILE        True if file is a named pipe.
      -r FILE        True if file is readable by you.
      -s FILE        True if file exists and is not empty.
      -S FILE        True if file is a socket.
      -t FD          True if FD is opened on a terminal.
      -u FILE        True if the file is set-user-id.
      -w FILE        True if the file is writable by you.
      -x FILE        True if the file is executable by you.
      -O FILE        True if the file is effectively owned by you.
      -G FILE        True if the file is effectively owned by your group.
      -N FILE        True if the file has been modified since it was last read.

      FILE1 -nt FILE2  True if file1 is newer than file2 (according to
                       modification date).

      FILE1 -ot FILE2  True if file1 is older than file2.

      FILE1 -ef FILE2  True if file1 is a hard link to file2.

    String operators:

      -z STRING      True if string is empty.

      -n STRING
         STRING      True if string is not empty.

      STRING1 = STRING2
                     True if the strings are equal.
      STRING1 != STRING2
                     True if the strings are not equal.
      STRING1 < STRING2
                     True if STRING1 sorts before STRING2 lexicographically.
      STRING1 > STRING2
                     True if STRING1 sorts after STRING2 lexicographically.

    Other operators:

      -o OPTION      True if the shell option OPTION is enabled.
      -v VAR     True if the shell variable VAR is set
      ! EXPR         True if expr is false.
      EXPR1 -a EXPR2 True if both expr1 AND expr2 are true.
      EXPR1 -o EXPR2 True if either expr1 OR expr2 is true.

      arg1 OP arg2   Arithmetic tests.  OP is one of -eq, -ne,
                     -lt, -le, -gt, or -ge.

    Arithmetic binary operators return true if ARG1 is equal, not-equal,
    less-than, less-than-or-equal, greater-than, or greater-than-or-equal
    than ARG2.

    Exit Status:
    Returns success if EXPR evaluates to true; fails if EXPR evaluates to
    false or an invalid argument is given.

1.3 判断文件

bash 复制代码
# test文件是否存在,根据变量的复制判断不同的结果
[harvy@CentOS7 ~ 09:54:50]$ test -a $file && echo "${file} is exist" || echo "${file} is not exist"
/etc/fstab is exist
[harvy@CentOS7 ~ 09:57:16]$ file=/etc/fstab-aaa
[harvy@CentOS7 ~ 09:57:29]$ test -a $file && echo "${file} is exist" || echo "${file} is not exist"
/etc/fstab-aaa is not exist

1.4 判断目录

bash 复制代码
# 目录存在,显示目录
[harvy@CentOS7 ~ 09:50:25]$ test -a $dir_path && echo "${dir_path} is exist" || echo "${dir_path} is not exist"
/home/harvy/test_dir is exist
# 将test目录删除
[harvy@CentOS7 ~ 09:54:30]$ rm -rf /home/harvy/test_dir/
# 再次test显示文件不存在
[harvy@CentOS7 ~ 09:54:48]$ test -a $dir_path && echo "${dir_path} is exist" || echo "${dir_path} is not exist"
/home/harvy/test_dir is not exist

1.5 其他示例

bash 复制代码
[harvy@CentOS7 ~ 09:44:46]$ ls -ld /etc/fstab /etc/shadow
-rw-r--r-- 1 root root  541 Apr 10 14:26 /etc/fstab
---------- 1 root root 1262 Apr 10 14:01 /etc/shadow
[harvy@CentOS7 ~ 09:48:41]$ test -r /etc/fstab  && tail -5 /etc/fstab || echo "You have no privilege for this file"
#
/dev/mapper/centos-root /                       xfs     defaults        0 0
UUID=a90d05c3-2ade-4aa6-bd7e-0781907fb198 /boot                   xfs     defaults        0 0
/dev/mapper/centos-home /home                   xfs     defaults        0 0
/dev/mapper/centos-swap swap                    swap    defaults        0 0
[harvy@CentOS7 ~ 09:50:10]$ test -r /etc/fstab  && tail -5 /etc/shadow || echo "You have no privilege for this file"
tail: cannot open '/etc/shadow' for reading: Permission denied
You have no privilege for this file

1.6 判断字符串

  • -z,字符串为空,则为true
  • -n,字符串非空,则为true
bash 复制代码
# 测试变量是否定义
[harvy@CentOS7 ~ 10:07:41]$ test -z "$testvar" && echo "testvar is not defined" || echo "testvar is ${testvar}"
testvar is not defined
[harvy@CentOS7 ~ 10:13:18]$ testvar=name
[harvy@CentOS7 ~ 10:13:35]$ test -z "$testvar" && echo "testvar is not defined" || echo "testvar is ${testvar}"
testvar is name

# 取消变量定义
[harvy@CentOS7 ~ 10:13:37]$ unset testvar 
[harvy@CentOS7 ~ 10:14:09]$ test -n "$testvar" && echo "testvar is ${testvar}" || testvar=zhangsan

# 再次打印,显示变量值
[harvy@CentOS7 ~ 10:15:01]$ test -n "$testvar" && echo "testvar is ${testvar}" || testvar=zhangsan
testvar is zhangsan
  • STRING1 = STRING2,True if the strings are equal

  • STRING1 != STRING2,True if the strings are equal.

bash 复制代码
# 检查当前运行的用户
# 如果不是root用户,则提示:请以root身份运行
# 如果是root用户,则执行创建用户zhangsan
  
#普通用户运行,则提示:请以root身份运行
[harvy@CentOS7 ~ 10:22:52]$ test "$USER" = "root" && useradd zhangsan || echo "Please run as root"
Please run as root
[harvy@CentOS7 ~ 10:31:13]$ id zhangsan
id: zhangsan: no such user

# root用户运行,创建用户成功
[root@CentOS7 ~ 10:32:36]# test "$USER" = "root" && useradd zhangsan || echo "Please run as root"
[root@CentOS7 ~ 10:32:41]# id zhangsan
uid=1001(zhangsan) gid=1001(zhangsan) groups=1001(zhangsan)

# 等效命令
# 先删除zhangsan2用户,连带文件目录一起删除
[root@CentOS7 ~ 10:35:14]# userdel -r zhangsan2

[root@CentOS7 ~ 10:35:36]# test "$USER" != "root" && echo "Please run as root" || useradd zhangsan2

[root@CentOS7 ~ 10:35:40]# id zhangsan2
uid=1002(zhangsan2) gid=1002(zhangsan2) groups=1002(zhangsan2)

1.7 数值判断

arg1 OP arg2 Arithmetic tests. OP is one of -eq, -ne, -lt, -le, -gt, or -ge.

bash 复制代码
# 检查当前运行的用户
# 如果不是root用户,则提示:请以root身份运行
# 如果是root用户,则执行删除用户zhangsan

# 普通用户运行,则提示:请以root身份运行
[harvy@CentOS7 ~ 10:39:42]$ test $UID -eq 0 && userdel -r zhangsan || echo "Please run as root"
Please run as root

# root用户运行,删除用户成功,zhangsan原本就不存在
[root@CentOS7 ~ 10:38:04]# test $UID -eq 0 && userdel -r zhangsan || echo "Please run as root"userdel: user 'zhangsan' does not exist
Please run as root

1.8 多条件测试

  • ! EXPR True if expr is false
  • EXPR1 -a EXPR2 True if both expr1 AND expr2 are true.
  • EXPR1 -o EXPR2 True if either expr1 OR expr2 is true.
bash 复制代码
# 逻辑非:提交取反满足
[harvy@CentOS7 ~ 11:21:28]$ test ! -a $file && touch $file && echo "create file success."
create file success.
[harvy@CentOS7 ~ 11:21:37]$ ls $file
/tmp/test.log

# 逻辑与:两个条件同时满足
[harvy@CentOS7 ~ 11:22:29]$ test -w $file -a -a $file && date > $file
[harvy@CentOS7 ~ 11:23:03]$ cat $file
Mon Apr 13 11:23:03 CST 2026

# 逻辑或:两个条件任一满足
[harvy@CentOS7 ~ 11:23:51]$ file1=/etc/yum.conf 
[harvy@CentOS7 ~ 11:24:03]$ file2=/etc/yum.repos.d/
[harvy@CentOS7 ~ 11:24:29]$ test -a $file1 -o -a $file2 && echo "yum repo is exist"
yum repo is exist

2. IF 语句

2.1 单分支语法

bash 复制代码
# 单分支
if 条件判断;then
  command
fi

if 条件判断
then
  command
fi

条件判断 && command

2.2双分支语法

bash 复制代码
# 单分支
if 条件判断;then
  command1
else
  command2
fi

条件判断 && command1 || command2

2.3 多分支语法

bash 复制代码
# 单分支
if 条件1判断;then
  command1
elif 条件2判断;then
  command2
else
  command3
fi

2.4 实践

双分支

需求:

  • 如果sshd服务正常运行,则提示服务正在运行
  • 提示服务未运行,并启动服务,启动后提示服务正常运行

开发脚本if_sshd.sh, 内容如下:

bash 复制代码
[root@CentOS7 ~ 14:15:47]# vim if_sshd.sh

#!/bin/bash
systemctl is-active sshd &> /dev/null
if (($?==0));then
  echo sshd is running
else
  echo sshd is not running,auto restart it. && systemctl start sshd
fi

运行结果

bash 复制代码
[root@CentOS7 ~ 14:30:52]# systemctl stop sshd
[root@CentOS7 ~ 14:31:04]# bash if_sshd_running2.sh 
sshd is not running,auto restart it.
[root@CentOS7 ~ 14:31:15]# bash if_sshd_running2.sh 
sshd is running

多分支

需求:开发if_sshd.sh脚本。

  • 脚本使用方法:"Usage:$0 start|stop|status|restart|reload"
  • 如果参数数量个数不是1个,则输出脚本使用方法。
  • 如果$1是start,则执行'systemctl start sshd'
  • 如果$1是stop,则执行'systemctl stop sshd'
  • 如果$1是status,则执行'systemctl status sshd'
  • 如果$1是restart,则执行'systemctl restart sshd'
  • 如果$1是reload,则执行'systemctl reload sshd'
  • 如果$1是其他子命令,则输出脚本使用方法。

开发脚本if_sshd.sh,内容如下:

bash 复制代码
#!/bin/bash
service_name=sshd

if (($#!=1));then
        echo "Usage: $0 start|stop|status|restart|reload"
        exit
fi

if [ "$1" == "start" ];then
        systemctl start ${service_name}
elif [ "$1" == "stop" ];then
        systemctl stop ${service_name}
elif [ "$1" == "restart" ];then
        systemctl restart ${service_name}
elif [ "$1" == "status" ];then
        systemctl status ${service_name}
elif [ "$1" == "reload" ];then
        systemctl reload ${service_name}

else
        echo "Usage: $0 start|stop|status|restart|reload"
fi

运行结果

bash 复制代码
# 缺乏参数
[root@CentOS7 ~ 14:52:40]# bash if_sshd.sh 
Usage: if_sshd.sh start|stop|status|restart|reload

# 参数过多
[root@CentOS7 ~ 14:53:06]# bash if_sshd.sh start stop
Usage: if_sshd.sh start|stop|status|restart|reload

# 正常传参
[root@CentOS7 ~ 14:53:13]# bash if_sshd.sh status
● sshd.service - OpenSSH server daemon
   Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)
   Active: active (running) since Mon 2026-04-13 14:31:15 CST; 22min ago
     Docs: man:sshd(8)
......略

# 参入错误参数
[root@CentOS7 ~ 14:54:14]# bash if_sshd.sh statrtrtr
Usage: if_sshd.sh start|stop|status|restart|reload

优化代码

bash 复制代码
#!/bin/bash

service_name="$2"
action="$1"

if (($#!=2));then
  echo "Usage:$0 start|stop|status|restart|reload ${service_name}"
  exit
fi

if [ "$action" == "start" -o "$action" == "stop" -o "$action" == "status"  -o "$action" == "restart" -o "$action" == "reload" ];then
  systemctl $action ${service_name}
else
  echo "Usage:$0 start|stop|status|restart|reload"
fi

3. FOR 语句

3.1 Shell 自带语法

bash 复制代码
for 变量名 in 清单
do
  command
done

3.2 C语言格式语法

bash 复制代码
for ((num=1;num<=10;num++))
do
  echo $num
done

3.3 实践

从1+2+...+n-1+n的和

bash 复制代码
vim cal_for.sh

代码具体内容

bash 复制代码
#!/bin/bash
maxnum="$1"
sum=0
num_n=0
for ((num=1;num<=$maxnum;num++))
do
        sum=$[ sum + num ]
done
num_n=$[ maxnum-1 ]
echo "1+2+3+...+$num_n+$maxnum=$sum"

执行效果

bash 复制代码
[root@CentOS7 ~ 15:32:45]# bash cal_for.sh 10
1+2+3+...+9+10=55
[root@CentOS7 ~ 15:33:06]# bash cal_for.sh 55
1+2+3+...+54+55=1540

批量修改文件名称

bash 复制代码
# 准备文件
[root@CentOS7 ~ 15:33:22]# mkdir Pictures
[root@CentOS7 ~ 15:48:46]# touch Pictures/snap-{01..10}.jpg
[root@CentOS7 ~ 15:49:01]# ls /root/Pictures/
snap-01.jpg  snap-03.jpg  snap-05.jpg  snap-07.jpg  snap-09.jpg
snap-02.jpg  snap-04.jpg  snap-06.jpg  snap-08.jpg  snap-10.jpg
bash 复制代码
# 代码内容
#!/bin/bash
for file in Pictures/snap*
do
  old_name=$file
  new_name=${old_name/snap/pic}
  mv ${old_name} ${new_name}
done
bash 复制代码
# 执行效果
[root@CentOS7 ~ 15:49:45]# vim mod_filename.sh
[root@CentOS7 ~ 15:53:17]# bash mod_filename.sh 
[root@CentOS7 ~ 15:53:27]# ls ~/Pictures/
pic-01.jpg  pic-03.jpg  pic-05.jpg  pic-07.jpg  pic-09.jpg
pic-02.jpg  pic-04.jpg  pic-06.jpg  pic-08.jpg  pic-10.jpg

为公司新入职的员工,创建一批账号

  • 员工姓名清单保存在staff.txt中,每行一个。
  • 为每个员工创建一个初始密码,初始密码是8位随机值,要求登录后必须修改密码。
  • 创建的结果要保存到users_info.conf中,格式为username: password。
bash 复制代码
# 代码内容如下:
#!/bin/bash
staff_name_file=staff.txt
staff_info_file=uaer_info.conf

if [ -a ${staff_name_file} -a -r ${staff_name_file} ];then
  for staff in $(cat $(staff_name_file))
    do
      useradd $staff
      password=$(date +%N| md5sum | head -c8)
      echo $password | passwd --stdin $staff &> /dev/null
      echo "$staff:$password" >> ${staff_info_file}
    done
else
  echo "${staff_name_file} is not exist."
fi

运行结果

bash 复制代码
[root@CentOS7 ~ 16:32:23]# bash add_staff.sh 
[root@CentOS7 ~ 16:32:25]# cat ~/user_info.conf 
Tom:b5e15742
Jerry:018095af
Jack:ac661597
Rose:0b5c5dce

优化代码:如果用户已经存在,则跳过创建

bash 复制代码
# 代码内容
#!/bin/bash
staff_name_file=staff.txt
staff_info_file=user_info.conf

if [ -a ${staff_name_file} -a -r ${staff_name_file} ];then
  for staff in $(cat ${staff_name_file})
  do
	id $staff &> /dev/null
	flag=$?
	if (($flag != 0));then
        useradd $staff
        password=$(date +%N | md5sum | head -c8)
        echo $password | passwd --stdin $staff &> /dev/null
        echo "$staff:$password" >> ${staff_info_file}
	else
	  echo "${staff} is exist,pass and go on"
	fi 
done
else
  echo "${staff_name_file} is not exist."
fi
bash 复制代码
# 运行结果
[root@CentOS7 ~ 17:10:05]# bash staff_v2.1.sh 
Tom is exist,pass and go on
Jerry is exist,pass and go on
Jack is exist,pass and go on
Rose is exist,pass and go on

3.4 生成随机值常见方法:

  1. 利用时间生成纳秒级别值:date +%N
bash 复制代码
[root@centos7 bin 16:04:25]# date +%N
456812617
  1. mktemp命令创建一个临时目录,目录名是随机值。
bash 复制代码
[root@centos7 bin 16:05:14]# echo $(mktemp)
/tmp/tmp.ncZkKK9TYD
  1. 利用环境变量RANDOM生成0~32767随机值。

4. while/until 语句

4.1 基本语法

while

条件满足,一直运行。

bash 复制代码
while 条件判断
do
  command
done

until

条件不满足,一直运行。满足条件,则终止运行。

bash 复制代码
until 条件判断
do
  command
done

4.2 实践demo

监控 sshd 服务

开发脚本monitor_sshd.sh 实时监控sshd服务,实现以下功能:

  1. 如果sshd服务没有运行,则报告sshd服务状态到/tmp/sshd_status.log日志;同时启动sshd服务,启动的结果也写入/tmp/sshd_status.log日志。
  2. 如果sshd服务正常运行,则报告sshd服务状态,并写入/tmp/sshd_status.log日志
  3. 每隔3秒执行一次监控。
bash 复制代码
#!/bin/bash

# 定义日志文件路径,所有监控信息都会记录到这个文件里
log_file=/tmp/monitor_sshd.log

# 无限循环:一直执行监控,永不退出
while true
do
  # 获取 sshd 服务的当前状态(active:运行中,inactive:停止,failed:故障)
  status=$(systemctl is-active sshd)
  
  # 判断:如果 sshd 状态是 active(正在运行)
  if [ "$status" = "active" ];then
    # 输出日志:打印当前时间 + 服务正常信息,同时写入日志文件(tee -a)
    echo "$(date):sshd is running,status is active." | tee -a ${log_file}
  else
    # 否则:sshd 没有运行,先输出提示日志
    echo "$(date):sshd is not running,status is not active." | tee -a ${log_file}
	
    # 尝试启动 sshd 服务,屏蔽所有输出信息
    systemctl start sshd &> /dev/null &&\
    # 如果启动成功(&& 代表上一条命令成功才执行),打印成功日志
    echo "$(date):Start sshd success."  | tee -a ${log_file} ||\
    # 如果启动失败(|| 代表上一条命令失败才执行),打印失败日志
    echo "$(date):Start sshd failed."  | tee -a ${log_file}
  fi

  # 每次检查完,休眠 3 秒,再进行下一次循环
  sleep 3
done

运行结果

bash 复制代码
[root@CentOS7 ~ 17:21:54]# bash moniter_sshd.sh 
Mon Apr 13 17:22:22 CST 2026:sshd is running,status is active.
Mon Apr 13 17:22:25 CST 2026:sshd is not running,status is not active.
Mon Apr 13 17:22:25 CST 2026:Start sshd success.
Mon Apr 13 17:22:28 CST 2026:sshd is running,status is active.
相关推荐
小比特_蓝光3 小时前
Linux----进程概念
linux·运维·服务器
大卡片3 小时前
Linux进程基础
linux·运维·服务器
.柒宇.3 小时前
docker容器技术实战
运维·docker·容器
优化Henry3 小时前
LTE-TDD小区光路闪断故障处理典型案例
运维·网络·5g·信息与通信
ShineWinsu3 小时前
对于Linux:“一切皆文件“以及缓冲区的解析
linux·运维·c++·面试·笔试·缓冲区·一切皆文件
倔强的胖蚂蚁3 小时前
信创企业级 openEuler 24 部署 docker-ce 全指南
运维·docker·云原生·容器
LinuxRos4 小时前
I2C子系统与驱动开发:从协议到实战
linux·人工智能·驱动开发·嵌入式硬件·物联网
Crazy CodeCrafter4 小时前
服装实体店现在还适合转电商吗?
大数据·运维·人工智能·经验分享·自动化·开源软件
西西弟4 小时前
网络编程基础之TCP循环服务器
运维·服务器·网络·网络协议·tcp/ip