until循环 #
一、until循环基础 #
1.1 基本语法 #
bash
until [ 条件 ]; do
命令
done
until循环与while循环相反:当条件为假时执行循环体,直到条件为真时退出。
1.2 while与until对比 #
bash
#!/bin/bash
# while循环:条件为真时执行
count=1
while [ $count -le 5 ]; do
echo "while: $count"
((count++))
done
# until循环:条件为假时执行
count=1
until [ $count -gt 5 ]; do
echo "until: $count"
((count++))
done
# 两者输出相同,但条件相反
1.3 简单示例 #
bash
#!/bin/bash
# 基本计数
count=1
until [ $count -gt 5 ]; do
echo "计数: $count"
((count++))
done
# 使用 (( ))
num=0
until (( num >= 5 )); do
echo "数字: $num"
((num++))
done
二、until循环应用 #
2.1 等待条件满足 #
bash
#!/bin/bash
# 等待服务启动
echo "等待nginx服务启动..."
until systemctl is-active --quiet nginx; do
echo "等待中..."
sleep 1
done
echo "nginx服务已启动"
# 等待文件创建
file="/tmp/signal.txt"
echo "等待文件 $file 创建..."
until [ -f "$file" ]; do
sleep 1
done
echo "文件已创建"
# 等待进程结束
pid=$$
echo "等待进程 $pid 结束..."
until ! kill -0 $pid 2>/dev/null; do
sleep 1
done
echo "进程已结束"
2.2 等待网络连接 #
bash
#!/bin/bash
# 等待网络可用
wait_for_network() {
local host="$1"
local max_attempts=30
local attempt=0
echo "等待网络连接: $host"
until ping -c 1 "$host" &>/dev/null; do
((attempt++))
if [ $attempt -ge $max_attempts ]; then
echo "网络连接超时"
return 1
fi
echo "尝试 $attempt/$max_attempts..."
sleep 1
done
echo "网络已连接"
return 0
}
wait_for_network "google.com"
# 等待端口可用
wait_for_port() {
local host="$1"
local port="$2"
until nc -z "$host" "$port" 2>/dev/null; do
echo "等待端口 $host:$port..."
sleep 1
done
echo "端口 $port 已可用"
}
wait_for_port "localhost" 8080
2.3 等待数据库连接 #
bash
#!/bin/bash
# 等待MySQL启动
wait_for_mysql() {
local host="${1:-localhost}"
local port="${2:-3306}"
local user="${3:-root}"
echo "等待MySQL服务..."
until mysql -h"$host" -P"$port" -u"$user" -e "SELECT 1" &>/dev/null; do
echo "MySQL未就绪,等待..."
sleep 2
done
echo "MySQL已就绪"
}
wait_for_mysql
# 等待Redis启动
wait_for_redis() {
local host="${1:-localhost}"
local port="${2:-6379}"
echo "等待Redis服务..."
until redis-cli -h "$host" -p "$port" ping &>/dev/null; do
echo "Redis未就绪,等待..."
sleep 1
done
echo "Redis已就绪"
}
wait_for_redis
2.4 重试机制 #
bash
#!/bin/bash
# 带重试的命令执行
retry_until_success() {
local max_attempts=$1
local interval=$2
shift 2
local command="$@"
local attempt=1
until eval "$command"; do
if [ $attempt -ge $max_attempts ]; then
echo "达到最大重试次数: $max_attempts"
return 1
fi
echo "尝试 $attempt 失败,${interval}秒后重试..."
sleep "$interval"
((attempt++))
done
echo "成功!"
return 0
}
retry_until_success 5 2 "curl -f http://example.com/api"
三、until循环高级用法 #
3.1 复杂条件判断 #
bash
#!/bin/bash
# 等待多个条件同时满足
wait_for_all() {
local services=("nginx" "mysql" "redis")
until systemctl is-active --quiet "${services[0]}" && \
systemctl is-active --quiet "${services[1]}" && \
systemctl is-active --quiet "${services[2]}"; do
echo "等待所有服务启动..."
sleep 2
done
echo "所有服务已启动"
}
# 等待任意条件满足
wait_for_any() {
local files=("file1.txt" "file2.txt" "file3.txt")
until [ -f "${files[0]}" ] || \
[ -f "${files[1]}" ] || \
[ -f "${files[2]}" ]; do
echo "等待任意文件创建..."
sleep 1
done
echo "有文件已创建"
}
3.2 带超时的等待 #
bash
#!/bin/bash
# 带超时的等待
wait_with_timeout() {
local condition="$1"
local timeout=$2
local elapsed=0
until eval "$condition"; do
if [ $elapsed -ge $timeout ]; then
echo "等待超时"
return 1
fi
sleep 1
((elapsed++))
echo "已等待 ${elapsed}/${timeout} 秒"
done
echo "条件满足"
return 0
}
# 等待文件创建(最多30秒)
wait_with_timeout "[ -f /tmp/test.txt ]" 30
# 等待服务启动(最多60秒)
wait_with_timeout "systemctl is-active --quiet nginx" 60
3.3 嵌套until循环 #
bash
#!/bin/bash
# 等待多个服务依次启动
services=("nginx" "mysql" "redis")
for service in "${services[@]}"; do
echo "启动 $service..."
systemctl start "$service"
until systemctl is-active --quiet "$service"; do
echo "等待 $service 启动..."
sleep 1
done
echo "$service 已启动"
done
echo "所有服务已启动"
四、实战示例 #
4.1 服务健康检查 #
bash
#!/bin/bash
health_check() {
local service=$1
local max_attempts=30
local attempt=0
echo "健康检查: $service"
until systemctl is-active --quiet "$service"; do
((attempt++))
if [ $attempt -ge $max_attempts ]; then
echo "服务 $service 启动失败"
return 1
fi
echo "等待 $service... ($attempt/$max_attempts)"
sleep 1
done
echo "服务 $service 运行正常"
return 0
}
# 检查多个服务
services=("nginx" "mysql" "redis")
all_ok=true
for service in "${services[@]}"; do
if ! health_check "$service"; then
all_ok=false
fi
done
if $all_ok; then
echo "所有服务正常"
else
echo "部分服务异常"
fi
4.2 文件监控 #
bash
#!/bin/bash
# 等待文件出现并处理
wait_and_process() {
local watch_dir="$1"
local pattern="$2"
echo "监控目录: $watch_dir"
echo "匹配模式: $pattern"
while true; do
# 等待匹配的文件出现
until ls "$watch_dir"/$pattern 1>/dev/null 2>&1; do
sleep 1
done
# 处理文件
for file in "$watch_dir"/$pattern; do
echo "处理文件: $file"
# 处理逻辑
mv "$file" "$file.processed"
done
done
}
wait_and_process "/tmp/input" "*.txt"
4.3 数据库初始化等待 #
bash
#!/bin/bash
wait_for_db() {
local host="${DB_HOST:-localhost}"
local port="${DB_PORT:-3306}"
local user="${DB_USER:-root}"
local password="${DB_PASSWORD:-}"
local max_attempts=60
local attempt=0
echo "等待数据库初始化..."
until mysql -h"$host" -P"$port" -u"$user" -p"$password" -e "SELECT 1" &>/dev/null; do
((attempt++))
if [ $attempt -ge $max_attempts ]; then
echo "数据库连接超时"
exit 1
fi
echo "等待数据库... ($attempt/$max_attempts)"
sleep 2
done
echo "数据库已就绪"
# 执行初始化脚本
echo "执行初始化脚本..."
mysql -h"$host" -P"$port" -u"$user" -p"$password" < init.sql
}
wait_for_db
4.4 容器启动等待 #
bash
#!/bin/bash
wait_for_container() {
local container="$1"
local timeout=60
local elapsed=0
echo "等待容器 $container 启动..."
until docker inspect -f '{{.State.Running}}' "$container" 2>/dev/null | grep -q "true"; do
if [ $elapsed -ge $timeout ]; then
echo "容器启动超时"
return 1
fi
sleep 1
((elapsed++))
printf "\r等待中... %ds/%ds" $elapsed $timeout
done
echo ""
echo "容器 $container 已启动"
return 0
}
# 等待容器健康
wait_for_healthy() {
local container="$1"
local timeout=120
local elapsed=0
until docker inspect -f '{{.State.Health.Status}}' "$container" 2>/dev/null | grep -q "healthy"; do
if [ $elapsed -ge $timeout ]; then
echo "容器健康检查超时"
return 1
fi
sleep 2
((elapsed+=2))
printf "\r健康检查中... %ds/%ds" $elapsed $timeout
done
echo ""
echo "容器 $container 健康"
return 0
}
wait_for_container "mysql"
wait_for_healthy "mysql"
五、until与while的选择 #
5.1 选择指南 #
| 场景 | 推荐使用 |
|---|---|
| 条件为真时执行 | while |
| 条件为假时执行 | until |
| 等待条件变为真 | until |
| 等待条件变为假 | while |
| 自然语言表达 | until更直观 |
5.2 示例对比 #
bash
#!/bin/bash
# 等待文件创建:until更直观
# until版本
until [ -f /tmp/ready ]; do
sleep 1
done
echo "文件已创建"
# while版本
while [ ! -f /tmp/ready ]; do
sleep 1
done
echo "文件已创建"
# 等待文件删除:while更直观
# while版本
while [ -f /tmp/lock ]; do
sleep 1
done
echo "锁已释放"
# until版本
until [ ! -f /tmp/lock ]; do
sleep 1
done
echo "锁已释放"
六、until循环最佳实践 #
6.1 避免无限等待 #
bash
#!/bin/bash
# 不推荐:可能无限等待
until [ -f /tmp/file ]; do
sleep 1
done
# 推荐:添加超时
timeout=60
elapsed=0
until [ -f /tmp/file ]; do
if [ $elapsed -ge $timeout ]; then
echo "等待超时"
exit 1
fi
sleep 1
((elapsed++))
done
6.2 添加进度提示 #
bash
#!/bin/bash
# 推荐:显示等待进度
attempt=0
max_attempts=30
until systemctl is-active --quiet nginx; do
((attempt++))
if [ $attempt -ge $max_attempts ]; then
echo "服务启动超时"
exit 1
fi
printf "\r等待服务启动... %d/%d" $attempt $max_attempts
sleep 1
done
echo ""
echo "服务已启动"
七、总结 #
7.1 until循环要点 #
| 要点 | 说明 |
|---|---|
| 基本语法 | until [ 条件 ]; do … done |
| 执行条件 | 条件为假时执行 |
| 退出条件 | 条件为真时退出 |
| 与while关系 | 条件相反 |
| 典型用途 | 等待条件满足 |
7.2 下一步 #
你已经掌握了until循环,接下来让我们学习 循环控制,了解break和continue的使用!
最后更新:2026-03-27