0%

循环语句

Shell 循环语句:for、while、until 及循环控制

循环是 Shell 脚本中实现重复执行任务的核心结构,Bash 提供了 forwhileuntil 三种循环语句,以及 breakcontinue 等控制命令。掌握这些工具能高效处理批量任务(如文件处理、日志分析等)。

for 循环:遍历列表或范围

for 循环适合已知循环对象集合的场景,有两种语法形式。

列表遍历语法(基础形式)

1
2
3
4
5
for 变量 in 列表
do
命令1
命令2
done
  • 列表:可以是直接给出的值、文件通配符、命令输出等。
  • 变量:每次循环从列表中取一个值赋值给变量。
示例 1:遍历固定值列表
1
2
3
4
5
6
#!/bin/bash
# 遍历一周的天数
for day in 周一 周二 周三 周四 周五 周六 周日
do
echo "今天是 $day"
done

输出:

1
2
3
4
今天是 周一
今天是 周二
...
今天是 周日
示例 2:遍历文件列表
1
2
3
4
5
6
#!/bin/bash
# 列出当前目录下所有.sh文件
for script in *.sh
do
echo "找到脚本:$script"
done
示例 3:遍历命令输出
1
2
3
4
5
6
#!/bin/bash
# 遍历系统用户(取/etc/passwd的第一列)
for user in $(cat /etc/passwd | cut -d: -f1)
do
echo "系统用户:$user"
done

C 语言风格语法(数值范围)

1
2
3
4
for (( 初始值; 循环条件; 变量变化 ))
do
命令
done

适合需要精确控制循环次数的场景(如从 1 循环到 100)。

示例:计算 1 到 10 的和
1
2
3
4
5
6
7
#!/bin/bash
sum=0
for (( i=1; i<=10; i++ ))
do
sum=$((sum + i)) # 累加
done
echo "1到10的和为:$sum" # 输出:55

字段分隔符(IFS)的影响

默认情况下,Shell 会以空格、制表符、换行符作为字段分隔符拆分列表。如需修改(如仅按换行符拆分),可临时修改 IFS 变量:

1
2
3
4
5
6
7
8
#!/bin/bash
# 按换行符拆分(处理含空格的文件名)
IFS=$'\n' # 仅将换行符作为分隔符
for file in $(ls "我的文档") # 目录名含空格
do
echo "文件:$file"
done
unset IFS # 恢复默认分隔符

while 循环:条件为真时循环

while 循环在条件为真时重复执行命令,适合未知循环次数但已知终止条件的场景。

语法

1
2
3
4
while [ 循环条件 ]
do
命令
done
示例 1:1 到 10 累加(与 for 循环对比)
1
2
3
4
5
6
7
8
9
#!/bin/bash
sum=0
i=1
while [ $i -le 10 ] # 当i<=10时循环
do
sum=$((sum + i))
i=$((i + 1)) # 变量自增
done
echo "1到10的和为:$sum" # 输出:55
示例 2:交互式输入循环
1
2
3
4
5
6
7
8
9
10
#!/bin/bash
# 持续接收用户输入,直到输入"exit"
while true # 条件恒为真,无限循环
do
read -p "请输入命令(输入exit退出):" cmd
if [ "$cmd" = "exit" ]; then
break # 退出循环
fi
echo "你输入了:$cmd"
done

until 循环:条件为假时循环

until 循环与 while 相反,条件为假时循环,条件为真时终止,适合 “直到某个条件满足才停止” 的场景。

语法

1
2
3
4
until [ 循环条件 ]
do
命令
done
示例:1 到 10 累加(与 while 对比)
1
2
3
4
5
6
7
8
9
#!/bin/bash
sum=0
i=1
until [ $i -gt 10 ] # 当i>10时停止(条件为真时终止)
do
sum=$((sum + i))
i=$((i + 1))
done
echo "1到10的和为:$sum" # 输出:55

循环控制:break、continue、exit

break:跳出循环

  • break:跳出当前循环,执行循环后的命令。
  • break n:跳出 n 层循环(默认 n=1,即最内层循环)。
示例:单层循环跳出
1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
for num in 1 2 3 4 5
do
if [ $num -eq 3 ]; then
break # 当num=3时跳出循环
fi
echo "num = $num"
done
# 输出:
# num = 1
# num = 2
示例:多层循环跳出
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
# 双层循环,break 2 跳出两层
for i in 1 2 3
do
for j in a b c
do
if [ "$j" = "b" ]; then
break 2 # 直接跳出两层循环
fi
echo "i=$i, j=$j"
done
done
# 输出:i=1, j=a

continue:跳过本次循环

跳过当前循环中的剩余命令,直接进入下一次循环。

示例:跳过特定值
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
for num in 1 2 3 4 5
do
if [ $num -eq 3 ]; then
continue # 当num=3时跳过本次循环
fi
echo "num = $num"
done
# 输出:
# num = 1
# num = 2
# num = 4
# num = 5

exit:退出脚本

直接终止整个脚本的执行,可指定退出状态码(exit 0 表示成功,非 0 表示失败)。

示例:遇到错误退出脚本
1
2
3
4
5
6
7
8
9
#!/bin/bash
for file in file1.txt file2.txt file3.txt
do
if [ ! -f "$file" ]; then
echo "错误:文件 $file 不存在!"
exit 1 # 非0状态码表示执行失败
fi
echo "处理文件:$file"
done

循环的典型应用场景

批量文件处理

1
2
3
4
5
6
7
#!/bin/bash
# 为所有.log文件添加时间戳后缀
for log in *.log
do
[ -f "$log" ] || continue # 跳过非文件
mv "$log" "${log%.log}_$(date +%F).log"
done

日志分析(统计关键词出现次数)

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
# 统计access.log中各IP的访问次数
declare -A ip_counts # 声明关联数组

while read line; do
ip=$(echo $line | awk '{print $1}') # 提取IP
ip_counts[$ip]=$(( ${ip_counts[$ip]:-0} + 1 )) # 累加计数
done < access.log # 从文件读取内容

# 输出结果
for ip in "${!ip_counts[@]}"; do
echo "$ip: ${ip_counts[$ip]}次"
done

定时任务(配合 sleep)

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
# 每5秒检查一次服务状态,共检查10次
count=0
while [ $count -lt 10 ]
do
echo "[$(date +%T)] 检查服务状态..."
# 实际场景中替换为真实的服务检查命令
sleep 5 # 暂停5秒
count=$((count + 1))
done

欢迎关注我的其它发布渠道