在Linux系统管理和自动化开发领域,Shell脚本是每个运维工程师的必备技能。而循环结构,正是让脚本从简单的单次执行升级为强大自动化工具的关键所在。我至今还记得刚入行时,手动一台台服务器执行命令的煎熬,直到掌握了循环语句,工作效率才有了质的飞跃。
Shell提供了三种核心循环结构:for、while和until。它们各有所长,就像工具箱里的不同工具:
这三种结构共同构成了Shell自动化的基础骨架。在企业级运维场景中,它们被广泛应用于:
for循环的核心思想是"遍历"——就像超市收银员一件件扫描商品。其基本语法结构如下:
bash复制for 变量 in 列表项
do
执行命令
done
这里的"列表项"可以有多种来源形式:
for i in 1 2 3 4 5for user in $(cat userlist.txt)for file in /var/log/*.logfor i in {1..10}for process in $(ps -ef | grep java)重要提示:当使用文件通配符时,如果可能匹配到包含空格的文件名,应该设置IFS变量并加上引号:
IFS=$'\n'; for file in *.log; do [...]
在企业环境中,经常需要批量创建或删除用户账号。下面是一个增强版的生产环境脚本:
bash复制#!/bin/bash
# 批量用户管理脚本
# 功能:根据用户列表文件创建账户,设置随机密码并记录日志
USER_LIST="/opt/scripts/userlist.txt"
LOG_FILE="/var/log/user_management.log"
PASSWORD_LENGTH=12
# 检查用户列表文件是否存在
if [ ! -f "$USER_LIST" ]; then
echo "$(date) - 错误:用户列表文件 $USER_LIST 不存在" | tee -a $LOG_FILE
exit 1
fi
# 主循环
for USERNAME in $(cat $USER_LIST)
do
# 检查用户是否已存在
if id "$USERNAME" &>/dev/null; then
echo "$(date) - 用户 $USERNAME 已存在,跳过创建" | tee -a $LOG_FILE
continue
fi
# 生成随机密码
PASSWORD=$(openssl rand -base64 $PASSWORD_LENGTH | tr -d '=+/' | cut -c1-$PASSWORD_LENGTH)
# 创建用户
useradd -m -s /bin/bash "$USERNAME"
echo "$USERNAME:$PASSWORD" | chpasswd
# 记录日志
echo "$(date) - 已创建用户: $USERNAME, 初始密码: $PASSWORD" | tee -a $LOG_FILE
# 强制首次登录修改密码
chage -d 0 "$USERNAME"
done
这个脚本相比基础版本增加了以下生产环境必需的功能:
运维工程师经常需要检查大批量服务器的存活状态。下面是一个增强版的服务器健康检查脚本:
bash复制#!/bin/bash
# 服务器集群健康检查脚本
# 功能:批量检查服务器连通性、磁盘空间和关键服务状态
SERVER_LIST="/opt/scripts/serverlist.txt"
TIMEOUT=3
CHECK_PORT=22
TEMP_FILE="/tmp/server_check.tmp"
# 检查依赖命令
if ! command -v nc &> /dev/null; then
echo "错误:需要安装netcat(nc)工具"
exit 1
fi
# 清空临时文件
> "$TEMP_FILE"
# 主检查循环
for SERVER in $(cat $SERVER_LIST)
do
echo "检查服务器: $SERVER" | tee -a "$TEMP_FILE"
# 连通性检查
if ping -c 1 -W $TIMEOUT "$SERVER" &> /dev/null; then
echo " - 网络连通性: 正常" | tee -a "$TEMP_FILE"
# 端口检查
if nc -z -w $TIMEOUT "$SERVER" $CHECK_PORT &> /dev/null; then
echo " - SSH端口($CHECK_PORT): 开放" | tee -a "$TEMP_FILE"
# 远程执行命令检查磁盘空间
DISK_INFO=$(ssh "$SERVER" "df -h / | tail -1")
echo " - 磁盘空间: $DISK_INFO" | tee -a "$TEMP_FILE"
# 检查关键服务
SERVICES=("nginx" "mysql" "redis")
for SERVICE in "${SERVICES[@]}"
do
if ssh "$SERVER" "systemctl is-active $SERVICE" &> /dev/null; then
echo " - 服务 $SERVICE: 运行中" | tee -a "$TEMP_FILE"
else
echo " - 服务 $SERVICE: 未运行" | tee -a "$TEMP_FILE"
fi
done
else
echo " - SSH端口($CHECK_PORT): 关闭" | tee -a "$TEMP_FILE"
fi
else
echo " - 网络连通性: 失败" | tee -a "$TEMP_FILE"
fi
echo "----------------------------------------" | tee -a "$TEMP_FILE"
done
# 生成最终报告
REPORT_FILE="/var/log/server_health_report_$(date +%Y%m%d).log"
mv "$TEMP_FILE" "$REPORT_FILE"
echo "检查完成,报告已保存至 $REPORT_FILE"
这个脚本的特点:
while循环就像一个不知疲倦的工人,只要条件满足就会持续工作。其基本结构为:
bash复制while [ 条件 ]
do
执行命令
done
条件测试可以是:
[ $i -lt 10 ][ "$input" != "quit" ][ -f "/tmp/lockfile" ]while ping -c 1 server &> /dev/null在生产环境中,关键服务的持续可用性至关重要。下面是一个服务监控脚本:
bash复制#!/bin/bash
# 关键服务监控脚本
# 功能:持续监控指定服务,异常时尝试自动恢复并报警
SERVICE="nginx"
CHECK_INTERVAL=60
MAX_RETRIES=3
ADMIN_EMAIL="admin@example.com"
LOCK_FILE="/tmp/service_monitor.lock"
# 防止脚本重复运行
if [ -f "$LOCK_FILE" ]; then
echo "监控脚本已在运行中 (锁定文件存在: $LOCK_FILE)"
exit 1
fi
touch "$LOCK_FILE"
trap 'rm -f "$LOCK_FILE"; exit' SIGINT SIGTERM
# 主监控循环
while true
do
# 检查服务状态
if ! systemctl is-active "$SERVICE" &> /dev/null; then
echo "$(date) - 服务 $SERVICE 已停止,尝试恢复..." | tee -a /var/log/service_monitor.log
RETRY=0
while [ $RETRY -lt $MAX_RETRIES ]
do
systemctl restart "$SERVICE"
sleep 10
if systemctl is-active "$SERVICE" &> /dev/null; then
echo "$(date) - 服务 $SERVICE 恢复成功" | tee -a /var/log/service_monitor.log
# 发送恢复通知
echo "服务 $SERVICE 于 $(date) 异常停止但已成功恢复" | \
mail -s "[重要] 服务恢复通知: $SERVICE" "$ADMIN_EMAIL"
break
fi
let RETRY++
echo "$(date) - 恢复尝试 $RETRY/$MAX_RETRIES 失败" | tee -a /var/log/service_monitor.log
done
if [ $RETRY -eq $MAX_RETRIES ]; then
echo "$(date) - 错误:无法恢复服务 $SERVICE" | tee -a /var/log/service_monitor.log
# 发送失败通知
echo "服务 $SERVICE 于 $(date) 异常停止,经过 $MAX_RETRIES 次尝试仍无法恢复" | \
mail -s "[紧急] 服务恢复失败: $SERVICE" "$ADMIN_EMAIL"
fi
fi
sleep $CHECK_INTERVAL
done
这个监控脚本的关键特性:
数据备份是运维工作的重中之重。下面是一个增强版的自动化备份脚本:
bash复制#!/bin/bash
# 自动化备份脚本
# 功能:轮转备份指定目录,保留最近N份备份
BACKUP_DIR="/data/backups"
SOURCE_DIR="/var/www/html"
RETENTION=7
COMPRESS_LEVEL=9
LOG_FILE="/var/log/backup.log"
# 检查目录存在性
[ -d "$SOURCE_DIR" ] || { echo "$(date) - 错误:源目录不存在 $SOURCE_DIR" | tee -a $LOG_FILE; exit 1; }
mkdir -p "$BACKUP_DIR"
# 生成备份文件名
BACKUP_FILE="$BACKUP_DIR/backup_$(date +%Y%m%d_%H%M%S).tar.gz"
# 执行备份
echo "$(date) - 开始备份 $SOURCE_DIR 到 $BACKUP_FILE" | tee -a $LOG_FILE
tar -czf "$BACKUP_FILE" --exclude='*.tmp' --exclude='cache/*' -C "$(dirname "$SOURCE_DIR")" "$(basename "$SOURCE_DIR")" &>> $LOG_FILE
if [ $? -eq 0 ]; then
echo "$(date) - 备份成功完成,大小: $(du -h "$BACKUP_FILE" | cut -f1)" | tee -a $LOG_FILE
# 备份轮转
echo "$(date) - 执行备份轮转,保留最近 $RETENTION 份备份" | tee -a $LOG_FILE
ls -t "$BACKUP_DIR"/backup_*.tar.gz | tail -n +$(($RETENTION+1)) | while read -r old_backup; do
echo "$(date) - 删除旧备份: $old_backup" | tee -a $LOG_FILE
rm -f "$old_backup"
done
else
echo "$(date) - 错误:备份失败" | tee -a $LOG_FILE
exit 1
fi
这个备份脚本的亮点:
until循环是while循环的"反面"——它会在条件为假时持续执行,直到条件为真。这种特性使其特别适合以下场景:
基本语法:
bash复制until [ 条件 ]
do
执行命令
done
在容器化部署中,经常需要等待依赖服务就绪。下面是一个等待数据库可用的脚本:
bash复制#!/bin/bash
# 数据库等待脚本
# 功能:等待数据库服务可用后再继续执行
DB_HOST="database"
DB_PORT=3306
MAX_ATTEMPTS=30
ATTEMPT=0
SLEEP_INTERVAL=5
echo "$(date) - 等待数据库 $DB_HOST:$DB_PORT 就绪..."
until nc -z -w 1 "$DB_HOST" "$DB_PORT" &> /dev/null || [ $ATTEMPT -eq $MAX_ATTEMPTS ]
do
let ATTEMPT++
echo "$(date) - 尝试 $ATTEMPT/$MAX_ATTEMPTS: 数据库尚未就绪,等待 $SLEEP_INTERVAL 秒..."
sleep $SLEEP_INTERVAL
done
if [ $ATTEMPT -eq $MAX_ATTEMPTS ]; then
echo "$(date) - 错误:数据库 $DB_HOST:$DB_PORT 在 $(($MAX_ATTEMPTS * $SLEEP_INTERVAL)) 秒后仍未就绪" >&2
exit 1
else
echo "$(date) - 数据库 $DB_HOST:$DB_PORT 已就绪"
fi
这个脚本的特点:
until循环非常适合处理用户交互场景。下面是一个增强版的用户菜单系统:
bash复制#!/bin/bash
# 交互式系统管理菜单
# 功能:提供常用管理功能的用户友好界面
show_menu() {
clear
echo "系统管理菜单"
echo "1. 显示系统信息"
echo "2. 显示磁盘使用"
echo "3. 显示内存使用"
echo "4. 列出运行中的服务"
echo "5. 重启系统"
echo "0. 退出"
}
process_choice() {
case $1 in
1)
echo -e "\n系统信息:"
uname -a
uptime
;;
2)
echo -e "\n磁盘使用情况:"
df -h
;;
3)
echo -e "\n内存使用情况:"
free -h
;;
4)
echo -e "\n运行中的服务:"
systemctl list-units --type=service --state=running
;;
5)
read -p "确认要重启系统吗?(y/n): " confirm
if [ "$confirm" = "y" ]; then
shutdown -r now
else
echo "取消重启"
fi
;;
0)
echo "退出菜单"
return 1
;;
*)
echo "无效选项,请重新输入"
;;
esac
read -p "按Enter键继续..."
return 0
}
# 主循环
until false
do
show_menu
read -p "请输入选项数字: " choice
process_choice "$choice" || break
done
echo "感谢使用系统管理菜单"
这个交互脚本的优势:
在实际使用循环时,有几个关键技巧可以大幅提升脚本的质量:
循环控制语句:
break:立即退出当前循环continue:跳过本次循环剩余部分,进入下一次循环break N/continue N:控制嵌套循环层级性能优化技巧:
xargs -P或parallel)错误处理增强:
bash复制for file in *.csv
do
if [ ! -f "$file" ]; then
echo "警告:未找到任何CSV文件" >&2
break
fi
if ! process_file "$file"; then
echo "错误:处理文件 $file 失败" >&2
continue
fi
done
嵌套循环在处理复杂任务时非常有用。下面是一个日志分析脚本示例:
bash复制#!/bin/bash
# 多服务器日志分析脚本
# 功能:分析多台服务器上多个日志文件的错误模式
SERVERS=("web1" "web2" "web3")
LOG_PATHS=("/var/log/nginx/error.log" "/var/log/app/error.log")
ERROR_PATTERNS=("500 Internal Server Error" "Connection refused" "Timeout")
for SERVER in "${SERVERS[@]}"
do
echo "===== 分析服务器: $SERVER ====="
for LOG_PATH in "${LOG_PATHS[@]}"
do
echo "检查日志文件: $LOG_PATH"
# 检查日志文件是否存在
if ! ssh "$SERVER" "[ -f '$LOG_PATH' ]"; then
echo " 日志文件不存在,跳过"
continue
fi
for PATTERN in "${ERROR_PATTERNS[@]}"
do
COUNT=$(ssh "$SERVER" "grep -c '$PATTERN' '$LOG_PATH' 2>/dev/null")
echo " 错误模式 '$PATTERN': 出现 $COUNT 次"
done
done
echo
done
在编写循环时,有几个常见陷阱需要注意:
变量作用域问题:
bash复制# 错误示例
cat file.txt | while read line
do
processed=$line
done
echo "$processed" # 为空,因为管道创建了子shell
# 正确做法
while read line
do
processed=$line
done < file.txt
echo "$processed"
特殊字符处理:
bash复制# 处理包含空格/特殊字符的文件名
find . -type f -print0 | while IFS= read -r -d '' file
do
echo "处理文件: $file"
done
调试技巧:
set -x开启调试模式echo语句输出变量值trap捕获信号进行清理下面是一个结合多种循环的自动化部署脚本:
bash复制#!/bin/bash
# 多服务器应用部署脚本
# 功能:在多台服务器上并行部署应用并验证
APP_NAME="myapp"
APP_VERSION="1.2.0"
DEPLOY_SERVERS=("web1" "web2" "web3" "web4")
DEPLOY_USER="deploy"
BUILD_DIR="/opt/builds/$APP_NAME-$APP_VERSION"
LOCK_DIR="/tmp/deploy_$APP_NAME.lock"
MAX_PARALLEL=2
DEPLOY_LOG="/var/log/deploy_${APP_NAME}_$(date +%Y%m%d).log"
# 创建锁目录
if ! mkdir "$LOCK_DIR" 2>/dev/null; then
echo "错误:部署已在进行中 (锁定目录存在: $LOCK_DIR)" >&2
exit 1
fi
trap 'rm -rf "$LOCK_DIR"' EXIT
# 检查构建目录
if [ ! -d "$BUILD_DIR" ]; then
echo "错误:构建目录不存在 $BUILD_DIR" | tee -a "$DEPLOY_LOG"
exit 1
fi
# 并行部署函数
deploy_to_server() {
local SERVER=$1
local START_TIME=$(date +%s)
echo "$(date) - 开始在 $SERVER 上部署 $APP_NAME $APP_VERSION" | tee -a "$DEPLOY_LOG"
# 传输文件
if ! rsync -az --delete "$BUILD_DIR/" "$DEPLOY_USER@$SERVER:/opt/$APP_NAME/"; then
echo "$(date) - 错误:文件传输到 $SERVER 失败" | tee -a "$DEPLOY_LOG"
return 1
fi
# 远程执行部署命令
if ! ssh "$DEPLOY_USER@$SERVER" "
cd /opt/$APP_NAME && \
./bin/setup.sh && \
sudo systemctl restart $APP_NAME
"; then
echo "$(date) - 错误:在 $SERVER 上执行部署命令失败" | tee -a "$DEPLOY_LOG"
return 1
fi
# 验证部署
local ATTEMPTS=0
local MAX_ATTEMPTS=5
until ssh "$DEPLOY_USER@$SERVER" "curl -s http://localhost:8080/health" | grep -q '"status":"UP"'
do
let ATTEMPTS++
if [ $ATTEMPTS -ge $MAX_ATTEMPTS ]; then
echo "$(date) - 错误:在 $SERVER 上验证部署失败" | tee -a "$DEPLOY_LOG"
return 1
fi
sleep 5
done
local END_TIME=$(date +%s)
local DURATION=$((END_TIME - START_TIME))
echo "$(date) - 成功:$SERVER 部署完成 (耗时 ${DURATION}秒)" | tee -a "$DEPLOY_LOG"
}
# 主部署流程
echo "$(date) - 开始部署 $APP_NAME 版本 $APP_VERSION" | tee -a "$DEPLOY_LOG"
# 使用有限并行度执行部署
SERVER_INDEX=0
while [ $SERVER_INDEX -lt ${#DEPLOY_SERVERS[@]} ]
do
RUNNING_JOBS=$(jobs -rp | wc -l)
if [ $RUNNING_JOBS -lt $MAX_PARALLEL ]; then
deploy_to_server "${DEPLOY_SERVERS[$SERVER_INDEX]}" &
let SERVER_INDEX++
else
sleep 1
fi
done
# 等待所有后台任务完成
wait
echo "$(date) - 部署流程完成" | tee -a "$DEPLOY_LOG"
# 检查是否有失败的部署
if grep -q "错误" "$DEPLOY_LOG"; then
echo "警告:部分服务器部署失败,请检查日志 $DEPLOY_LOG" >&2
exit 1
fi
这个部署脚本的特点:
日志分析是运维日常工作的重要部分。下面是一个综合日志分析脚本:
bash复制#!/bin/bash
# 综合日志分析脚本
# 功能:分析多类型日志并生成HTML报告
LOG_DIR="/var/log"
REPORT_DIR="/opt/reports"
REPORT_FILE="$REPORT_DIR/log_report_$(date +%Y%m%d).html"
ERROR_THRESHOLD=10
TOP_LIMIT=10
# 确保报告目录存在
mkdir -p "$REPORT_DIR"
# HTML报告头部
cat > "$REPORT_FILE" <<EOF
<!DOCTYPE html>
<html>
<head>
<title>系统日志分析报告 - $(date +%Y-%m-%d)</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
h1 { color: #333; }
table { border-collapse: collapse; width: 100%; margin-bottom: 20px; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
tr:nth-child(even) { background-color: #f9f9f9; }
.critical { background-color: #ffdddd; }
.warning { background-color: #fff3cd; }
</style>
</head>
<body>
<h1>系统日志分析报告 - $(date +%Y-%m-%d)</h1>
EOF
# 分析Nginx访问日志
if [ -f "$LOG_DIR/nginx/access.log" ]; then
echo "<h2>Nginx访问统计</h2>" >> "$REPORT_FILE"
# 统计TOP访问IP
echo "<h3>访问量TOP $TOP_LIMIT IP</h3>" >> "$REPORT_FILE"
echo "<table><tr><th>IP地址</th><th>访问次数</th></tr>" >> "$REPORT_FILE"
awk '{print $1}' "$LOG_DIR/nginx/access.log" | sort | uniq -c | sort -nr | head -n $TOP_LIMIT | while read -r count ip
do
echo "<tr><td>$ip</td><td>$count</td></tr>" >> "$REPORT_FILE"
done
echo "</table>" >> "$REPORT_FILE"
# 统计HTTP状态码
echo "<h3>HTTP状态码分布</h3>" >> "$REPORT_FILE"
echo "<table><tr><th>状态码</th><th>数量</th></tr>" >> "$REPORT_FILE"
awk '{print $9}' "$LOG_DIR/nginx/access.log" | sort | uniq -c | while read -r count code
do
if [ "$code" -ge 500 ]; then
class="critical"
elif [ "$code" -ge 400 ]; then
class="warning"
else
class=""
fi
echo "<tr class=\"$class\"><td>$code</td><td>$count</td></tr>" >> "$REPORT_FILE"
done
echo "</table>" >> "$REPORT_FILE"
fi
# 分析系统日志中的错误
echo "<h2>系统错误统计</h2>" >> "$REPORT_FILE"
echo "<table><tr><th>服务</th><th>错误类型</th><th>出现次数</th></tr>" >> "$REPORT_FILE"
find "$LOG_DIR" -type f \( -name "*.log" -o -name "messages" -o -name "syslog" \) -exec grep -iE "error|fail|exception" {} \; | \
awk '{
if ($0 ~ /error/) { type="ERROR" }
else if ($0 ~ /fail/) { type="FAILURE" }
else { type="EXCEPTION" }
# 提取服务名
if (FILENAME ~ /nginx/) { service="nginx" }
else if (FILENAME ~ /mysql/) { service="mysql" }
else { service="system" }
# 简化错误信息
gsub(/^.*error: /, "", $0)
gsub(/^.*exception: /, "", $0)
if (length($0) > 100) { $0 = substr($0, 1, 100) "..." }
print service "|" type "|" $0
}' | sort | uniq -c | sort -nr | head -n $TOP_LIMIT | while read -r count entry
do
service=$(echo "$entry" | cut -d'|' -f1)
type=$(echo "$entry" | cut -d'|' -f2)
message=$(echo "$entry" | cut -d'|' -f3)
if [ "$count" -gt $ERROR_THRESHOLD ]; then
class="critical"
else
class=""
fi
echo "<tr class=\"$class\"><td>$service</td><td>$type</td><td>$count - $message</td></tr>" >> "$REPORT_FILE"
done
echo "</table>" >> "$REPORT_FILE"
# HTML报告尾部
cat >> "$REPORT_FILE" <<EOF
<p>报告生成时间: $(date)</p>
</body>
</html>
EOF
echo "日志分析完成,报告已生成: $REPORT_FILE"
这个日志分析脚本的特点:
在处理大规模数据时,循环性能变得至关重要。以下是一些关键优化技巧:
减少子shell创建:
bash复制# 较慢 - 创建子shell
cat file.txt | while read line; do [...] done
# 更快 - 避免管道
while read line; do [...] done < file.txt
使用内置字符串操作:
bash复制# 较慢 - 调用外部命令
for file in $(ls *.log); do [...] done
# 更快 - 使用shell扩展
for file in *.log; do [...] done
批量处理替代逐行处理:
bash复制# 较慢 - 逐行处理
while read line; do process "$line"; done < data.txt
# 更快 - 批量处理
process < data.txt
并行处理示例:
bash复制# 使用xargs并行处理
find . -name "*.log" -print0 | xargs -0 -P 4 -n 1 process_log
# 使用GNU parallel
parallel -j 4 process_log ::: *.log
生产环境脚本必须有完善的错误处理:
设置错误退出:
bash复制set -euo pipefail
自定义错误处理:
bash复制handle_error() {
echo "错误发生在第 $1 行: $2" >&2
# 清理资源
exit 1
}
trap 'handle_error $LINENO "$BASH_COMMAND"' ERR
循环中的错误处理:
bash复制for server in "${servers[@]}"
do
if ! ping -c 1 "$server"; then
echo "服务器 $server 不可达" >&2
continue
fi
if ! ssh "$server" "command"; then
echo "在 $server 上执行命令失败" >&2
record_failure "$server"
continue
fi
done
超时控制:
bash复制for task in "${tasks[@]}"
do
timeout 60s process "$task" || echo "任务 $task 超时" >&2
done
将常用循环模式封装成函数可以大大提高脚本开发效率。下面是一个实用的循环工具库示例:
bash复制#!/bin/bash
# 循环工具函数库
# 包含常用循环模式的高阶函数
# 并行执行函数
# 用法: parallel_for "函数名" "数组变量名" "最大并行数"
parallel_for() {
local func=$1
local -n arr=$2
local max_parallel=${3:-5}
local running=0
for item in "${arr[@]}"
do
while [ $running -ge $max_parallel ]
do
sleep 0.1
running=$(jobs -rp | wc -l)
done
$func "$item" &
let running++
done
wait
}
# 重试循环
# 用法: retry "最大重试次数" "间隔秒数" "命令"
retry() {
local max=$1
local delay=$2
shift 2
local count=0
until "$@"
do
let count++
if [ $count -ge $max ]; then
echo "错误:达到最大重试次数 $max" >&2
return 1
fi
sleep $delay
done
}
# 进度条循环
# 用法: progress_for "数组变量名" "处理函数"
progress_for() {
local -n arr=$1
local func=$2
local total=${#arr[@]}
local current=0
for item in "${arr[@]}"
do
let current++
echo -ne "进度: [$current/$total] $(($current*100/$total))%\r"
$func "$item"
done
echo
}
# 超时循环
# 用法: timeout_for "超时秒数" "数组变量名" "处理函数"
timeout_for() {
local timeout=$1
local -n arr=$2
local func=$3
local start_time=$(date +%s)
for item in "${arr[@]}"
do
$func "$item"
if [ $(($(date +%s) - start_time)) -ge $timeout ]; then
echo "警告:处理超时" >&2
break
fi
done
}
# 示例用法
# 定义处理函数
process_item() {
echo "处理: $1"
sleep 1
}
# 定义数组
items=("item1" "item2" "item3" "item4" "item5")
# 使用并行处理
echo "并行处理示例:"
parallel_for "process_item" "items" 2
# 使用进度条
echo "进度条示例:"
progress_for "items" "process_item"
# 使用超时控制
echo "超时控制示例:"
timeout_for 3 "items" "process_item"
这个工具库提供了:
不同Shell实现(bash、zsh、dash等)对循环的支持略有差异:
数字循环语法:
bash复制# bash/zsh/ksh
for ((i=0; i<10; i++)); do [...] done
# dash/posix
i=0; while [ $i -lt 10 ]; do [...] done
数组处理差异:
bash复制# bash/zsh/ksh
arr=(a b c)
for item in "${arr[@]}"; do [...] done
# dash (不支持数组)
set -- a b c
for item in "$@"; do [...] done
范围表达式:
bash复制# bash/zsh
for i in {1..10}; do [...] done
# dash (