wait 命令 #
wait
命令用于等待指定的进程完成并返回退出状态。它主要在shell脚本中使用,用于同步进程执行,特别是在处理后台任务时。
语法 #
wait [选项] [ID...]
常用选项 #
选项 | 描述 |
---|---|
-n |
等待任何作业完成并返回其退出状态(Bash 4.3+) |
-f |
等待指定的PID完成,即使它不是当前shell的子进程(Bash 5.1+) |
-p 变量 |
将最后等待的进程的PID存储在指定的变量中(Bash 4.3+) |
参数 #
ID
:要等待的进程ID或作业规范(如%1
、%2
等)。如果未指定,则等待所有当前活动的子进程。
退出状态 #
- 如果未指定ID,返回0
- 如果所有指定的ID都成功完成,返回0
- 如果任何指定的ID未成功完成,返回最后一个失败的命令的退出状态
- 如果指定的ID不存在或无效,返回127
- 如果
wait
本身被信号中断,返回128+信号编号
常见用法 #
1. 等待所有后台进程完成 #
#!/bin/bash
command1 &
command2 &
command3 &
wait
echo "所有命令已完成"
这将启动三个后台进程,然后等待它们全部完成后继续执行。
2. 等待特定进程完成 #
#!/bin/bash
long_running_command &
pid=$!
echo "进程 $pid 正在后台运行"
wait $pid
echo "进程 $pid 已完成,退出状态: $?"
这将启动一个后台进程,记录其PID,然后等待它完成。
3. 等待任何作业完成 #
#!/bin/bash
command1 &
command2 &
command3 &
wait -n
echo "一个命令已完成,退出状态: $?"
这将等待任何一个后台进程完成,然后继续执行。
4. 存储完成进程的PID #
#!/bin/bash
command1 &
command2 &
wait -n -p finished_pid
echo "进程 $finished_pid 已完成,退出状态: $?"
这将等待任何一个后台进程完成,并将其PID存储在变量finished_pid
中。
5. 等待作业完成 #
#!/bin/bash
command1 &
command2 &
jobs
wait %1 # 等待第一个作业完成
echo "第一个作业已完成"
这将等待通过作业控制编号指定的作业完成。
实用示例 #
1. 并行处理文件 #
#!/bin/bash
for file in *.txt; do
process_file "$file" &
done
wait
echo "所有文件处理完成"
这将并行处理所有文本文件,然后等待所有处理完成。
2. 超时处理 #
#!/bin/bash
command &
pid=$!
( sleep 10; kill $pid 2>/dev/null ) &
watchdog_pid=$!
wait $pid
kill $watchdog_pid 2>/dev/null # 如果命令在超时前完成,取消watchdog
这将启动一个命令,如果它在10秒内没有完成,则终止它。
3. 收集多个进程的退出状态 #
#!/bin/bash
command1 &
pid1=$!
command2 &
pid2=$!
command3 &
pid3=$!
wait $pid1
status1=$?
wait $pid2
status2=$?
wait $pid3
status3=$?
echo "命令1退出状态: $status1"
echo "命令2退出状态: $status2"
echo "命令3退出状态: $status3"
if [ $status1 -eq 0 ] && [ $status2 -eq 0 ] && [ $status3 -eq 0 ]; then
echo "所有命令成功完成"
else
echo "一个或多个命令失败"
exit 1
fi
这将启动三个后台进程,等待它们完成,并收集它们的退出状态。
4. 实现简单的并行限制 #
#!/bin/bash
max_parallel=4
count=0
for job in job1 job2 job3 job4 job5 job6 job7 job8; do
# 如果达到最大并行数,等待任何一个作业完成
if [ $count -ge $max_parallel ]; then
wait -n
count=$((count - 1))
fi
# 启动新作业
echo "启动 $job"
do_job $job &
count=$((count + 1))
done
# 等待所有剩余作业完成
wait
echo "所有作业完成"
这将限制同时运行的作业数量,实现简单的并行控制。
5. 处理信号和清理 #
#!/bin/bash
# 设置清理函数
cleanup() {
echo "接收到信号,清理并退出"
kill $(jobs -p) 2>/dev/null
exit 1
}
# 捕获信号
trap cleanup SIGINT SIGTERM
# 启动后台进程
command1 &
command2 &
command3 &
# 等待所有进程完成
wait
echo "所有命令完成"
这将启动多个后台进程,并在收到中断信号时清理它们。
6. 实现简单的进度报告 #
#!/bin/bash
total_jobs=10
completed=0
for i in $(seq 1 $total_jobs); do
(
echo "作业 $i 开始"
sleep $((RANDOM % 5 + 1)) # 模拟工作
echo "作业 $i 完成"
) &
pids[$i]=$!
done
# 等待所有作业完成并报告进度
while [ $completed -lt $total_jobs ]; do
wait -n
completed=$((completed + 1))
echo "进度: $completed/$total_jobs ($(( completed * 100 / total_jobs ))%)"
done
echo "所有作业完成"
这将启动多个后台作业,并在每个作业完成时报告进度。
7. 并行执行命令并收集输出 #
#!/bin/bash
# 创建临时目录存储输出
tmp_dir=$(mktemp -d)
trap 'rm -rf "$tmp_dir"' EXIT
# 启动后台命令并重定向输出
command1 > "$tmp_dir/output1" 2> "$tmp_dir/error1" &
pid1=$!
command2 > "$tmp_dir/output2" 2> "$tmp_dir/error2" &
pid2=$!
# 等待命令完成
wait $pid1 $pid2
# 处理输出
echo "命令1输出:"
cat "$tmp_dir/output1"
echo "命令1错误:"
cat "$tmp_dir/error1"
echo "命令2输出:"
cat "$tmp_dir/output2"
echo "命令2错误:"
cat "$tmp_dir/error2"
这将并行执行命令,收集它们的标准输出和错误输出,然后在所有命令完成后处理这些输出。
8. 实现简单的作业队列 #
#!/bin/bash
# 创建命名管道
pipe=$(mktemp -u)
mkfifo $pipe
exec 3<>$pipe
rm $pipe
# 设置最大并行作业数
max_jobs=4
for ((i=1; i<=max_jobs; i++)); do
echo >&3
done
# 处理作业队列
for job in job1 job2 job3 job4 job5 job6 job7 job8 job9 job10; do
# 等待一个槽位可用
read -u 3
# 在后台处理作业并在完成时释放槽位
(
echo "处理 $job"
sleep $((RANDOM % 5 + 1)) # 模拟工作
echo "完成 $job"
echo >&3 # 释放槽位
) &
done
# 等待所有作业完成
wait
exec 3>&- # 关闭管道
echo "所有作业完成"
这实现了一个简单的作业队列,限制同时运行的作业数量。
与其他命令的比较 #
命令 | 功能 | 区别 |
---|---|---|
wait |
等待进程完成 | 内置命令,适用于shell脚本中的进程同步 |
sleep |
暂停执行指定时间 | 基于时间而不是进程状态 |
fg |
将后台作业移到前台 | 交互式命令,不适用于脚本 |
jobs |
列出活动作业 | 仅显示信息,不等待完成 |
kill -0 |
检查进程是否存在 | 不等待进程完成 |
timeout |
在指定时间后终止命令 | 基于时间限制,而不是等待完成 |
提示 #
wait
命令只能等待当前shell的子进程,除非使用-f
选项(Bash 5.1+)- 在脚本中使用
wait
可以确保所有后台任务完成后再继续执行或退出 - 使用
$!
变量可以获取最近启动的后台进程的PID - 在处理大量并行任务时,考虑使用作业队列或限制同时运行的任务数量
- 结合
trap
命令可以确保在脚本退出时清理所有后台进程 - 使用
wait -n
可以实现"完成一个处理一个"的工作流 - 在循环中启动后台进程时,注意变量作用域,可能需要使用子shell或局部变量
- 对于需要收集输出的并行任务,考虑将输出重定向到文件
- 在处理信号时,确保在
trap
处理程序中调用wait
以避免留下僵尸进程 - 使用
jobs -p
可以获取所有当前后台作业的PID列表