Shell脚本循环结构#
循环结构是Shell脚本中处理重复任务的核心机制,允许脚本根据条件重复执行一系列命令。本教程将详细介绍Shell中的各种循环结构,从基础的for循环到复杂的嵌套循环和循环控制。
入门#
基本for循环#
for循环是Shell中最常用的循环结构之一,用于遍历列表中的元素。
# 基本for循环
for variable in item1 item2 item3; do
# 执行命令
echo $variable
done
# 示例:遍历数字
for i in 1 2 3 4 5; do
echo "数字: $i"
done
# 示例:遍历字符串
for fruit in apple banana cherry; do
echo "水果: $fruit"
done
# 示例:遍历命令输出
for file in $(ls *.txt); do
echo "文件: $file"
done基本while循环#
while循环会在条件为真时重复执行命令,直到条件变为假。
# 基本while循环
while [ condition ]; do
# 执行命令
done
# 示例:计数器循环
count=1
while [ $count -le 5 ]; do
echo "计数: $count"
count=$((count + 1))
done
# 示例:读取文件内容
while read line; do
echo "行: $line"
done < file.txt
# 示例:无限循环
while true; do
echo "按Ctrl+C退出"
sleep 1
done中级#
高级for循环#
C风格for循环#
# C风格for循环
for ((i=1; i<=5; i++)); do
echo "数字: $i"
done
# 带步长的循环
for ((i=1; i<=10; i+=2)); do
echo "奇数: $i"
done
# 反向循环
for ((i=5; i>=1; i--)); do
echo "倒计时: $i"
done通配符和序列#
# 使用通配符
for file in *.txt; do
echo "文本文件: $file"
done
# 使用序列表达式
for i in {1..5}; do
echo "数字: $i"
done
# 带步长的序列
for i in {1..10..2}; do
echo "奇数: $i"
done
# 字母序列
for letter in {a..e}; do
echo "字母: $letter"
doneuntil循环#
until循环与while循环相反,会在条件为假时重复执行命令,直到条件变为真。
# 基本until循环
until [ condition ]; do
# 执行命令
done
# 示例:计数器循环
count=1
until [ $count -gt 5 ]; do
echo "计数: $count"
count=$((count + 1))
done
# 示例:等待文件创建
until [ -f "flag.txt" ]; do
echo "等待flag.txt创建..."
sleep 1
done
echo "flag.txt已创建"高级#
循环控制#
break命令#
break命令用于跳出当前循环,终止循环的执行。
# 基本break
for i in {1..10}; do
if [ $i -eq 5 ]; then
break # 跳出循环
fi
echo "数字: $i"
done
# 跳出嵌套循环
for i in {1..3}; do
for j in {1..3}; do
if [ $j -eq 2 ]; then
break # 只跳出内层循环
fi
echo "i=$i, j=$j"
done
done
# 跳出指定层数的循环
for i in {1..3}; do
for j in {1..3}; do
for k in {1..3}; do
if [ $k -eq 2 ]; then
break 3 # 跳出所有三层循环
fi
echo "i=$i, j=$j, k=$k"
done
done
donecontinue命令#
continue命令用于跳过当前循环的剩余部分,直接开始下一次循环。
# 基本continue
for i in {1..5}; do
if [ $i -eq 3 ]; then
continue # 跳过当前迭代
fi
echo "数字: $i"
done
# 跳过奇数
for i in {1..10}; do
if [ $((i % 2)) -eq 1 ]; then
continue
fi
echo "偶数: $i"
done
# 跳过空文件
for file in *.txt; do
if [ ! -s "$file" ]; then
continue
fi
echo "非空文件: $file"
donereturn和exit命令#
# 在函数中使用return
my_function() {
for i in {1..5}; do
if [ $i -eq 3 ]; then
return # 从函数返回
fi
echo "函数内: $i"
done
}
my_function
# 使用exit退出脚本
for i in {1..5}; do
if [ $i -eq 3 ]; then
echo "退出脚本"
exit # 退出整个脚本
fi
echo "脚本内: $i"
done嵌套循环#
# 基本嵌套循环
for i in {1..3}; do
for j in {1..3}; do
echo "i=$i, j=$j"
done
done
# 九九乘法表
for i in {1..9}; do
for j in {1..9}; do
if [ $j -le $i ]; then
result=$((i * j))
echo -n "$i*$j=$result "
fi
done
echo # 换行
done
# 目录和文件遍历
for dir in */; do
echo "目录: $dir"
for file in "$dir"*.txt; do
if [ -f "$file" ]; then
echo " 文件: $(basename $file)"
fi
done
done大师#
高级循环技巧#
进程替换循环#
# 使用进程替换
while read line; do
echo "处理: $line"
done < <(ls -la)
# 同时读取多个文件
while read -u 3 line1 && read -u 4 line2; do
echo "文件1: $line1"
echo "文件2: $line2"
done 3< file1.txt 4< file2.txt数组遍历#
# 遍历数组
fruits=(apple banana cherry date)
for fruit in "${fruits[@]}"; do
echo "水果: $fruit"
done
# 使用索引遍历数组
for ((i=0; i<${#fruits[@]}; i++)); do
echo "索引$i: ${fruits[$i]}"
done
# 关联数组遍历
declare -A colors
colors["red"]="#FF0000"
colors["green"]="#00FF00"
colors["blue"]="#0000FF"
for color in "${!colors[@]}"; do
echo "$color: ${colors[$color]}"
done并行循环#
# 使用后台进程并行执行
for i in {1..5}; do
(echo "任务$i开始"; sleep 2; echo "任务$i完成") &
done
wait # 等待所有后台进程完成
echo "所有任务完成"
# 限制并行数量
max_jobs=3
current_jobs=0
for i in {1..10}; do
(echo "任务$i开始"; sleep 1; echo "任务$i完成") &
current_jobs=$((current_jobs + 1))
if [ $current_jobs -ge $max_jobs ]; then
wait # 等待所有后台进程完成
current_jobs=0
fi
done
wait
echo "所有任务完成"性能优化#
# 减少循环内的命令执行
# 不好的做法
for i in {1..1000}; do
echo "数字: $i"
done
# 好的做法(使用缓冲区)
buffer=""
for i in {1..1000}; do
buffer+="数字: $i\n"
done
echo "$buffer"
# 避免循环内的文件I/O
# 不好的做法
for i in {1..1000}; do
echo "数字: $i" >> output.txt
done
# 好的做法
{ for i in {1..1000}; do
echo "数字: $i"
done } > output.txt
# 使用更快的循环结构
# C风格for循环比传统for循环快
for ((i=1; i<=1000000; i++)); do
: # 空命令
done
# 避免子shell
# 不好的做法(每次循环创建子shell)
for i in $(seq 1 1000); do
echo $i
done
# 好的做法
for i in {1..1000}; do
echo $i
done无敌#
高级循环应用#
事件驱动循环#
# 事件驱动循环
while true; do
read -t 1 -n 1 key
if [ $? -eq 0 ]; then
case $key in
q)
echo "退出"
exit 0
;;
*)
echo "按下: $key"
;;
esac
fi
# 其他处理代码
echo -n "."
done状态机循环#
# 简单状态机
state="idle"
while true; do
case $state in
idle)
echo "等待输入..."
read command
case $command in
start)
state="running"
;;
quit)
state="exiting"
;;
esac
;;
running)
echo "运行中..."
sleep 1
# 检查是否需要切换状态
if [ -f "stop.txt" ]; then
state="idle"
rm stop.txt
fi
;;
exiting)
echo "退出中..."
exit 0
;;
esac
done实用脚本示例#
批量文件处理脚本#
#!/bin/bash
# 批量重命名文件
echo "批量重命名.txt文件为.bak文件"
for file in *.txt; do
if [ -f "$file" ]; then
new_name="${file%.txt}.bak"
echo "重命名: $file -> $new_name"
mv "$file" "$new_name"
fi
done
echo "重命名完成"系统监控脚本#
#!/bin/bash
# 系统资源监控脚本
interval=5 # 监控间隔(秒)
count=10 # 监控次数
for ((i=1; i<=count; i++)); do
echo "===== 监控第$i次 ====="
# 查看CPU使用率
echo "CPU使用率:"
top -bn1 | grep "Cpu(s)"
# 查看内存使用
echo "内存使用:"
free -h
# 查看磁盘使用
echo "磁盘使用:"
df -h
# 查看网络连接
echo "网络连接数:"
netstat -tuln | wc -l
if [ $i -lt $count ]; then
echo "等待${interval}秒..."
sleep $interval
fi
done
echo "监控完成"日志分析脚本#
#!/bin/bash
# 日志分析脚本
log_file="/var/log/syslog"
# 统计不同级别的日志
levels=()
while read line; do
# 提取日志级别
level=$(echo $line | grep -o "[A-Z]\{3,5\}" | head -n 1)
if [ -n "$level" ]; then
levels+=("$level")
fi
done < "$log_file"
# 统计并排序
echo "日志级别统计:"
printf "%s\n" "${levels[@]}" | sort | uniq -c | sort -nr
# 统计错误数量
error_count=$(grep -c "ERROR" "$log_file")
echo "错误数量: $error_count"
# 显示最近的10条错误
echo "最近的10条错误:"
grep "ERROR" "$log_file" | tail -n 10总结#
循环结构是Shell脚本中处理重复任务的强大工具,通过掌握for、while、until等循环结构以及break、continue等循环控制语句,您可以创建更加灵活和高效的Shell脚本。从简单的文件遍历到复杂的系统监控,循环结构在各种场景中都发挥着重要作用。
随着您对Shell循环结构的理解不断深入,您将能够编写更加健壮、高效的Shell脚本,处理各种复杂的任务和场景。记住,合理使用循环结构可以大大提高脚本的自动化程度和执行效率。