如何让Shell脚本等待已调度的at任务完成后再继续执行后续代码
为什么之前用
wait达不到预期 Shell内置的wait有明确的作用边界:它只能等待当前Shell直接衍生的子进程退出。而at提交的任务是完全交给系统的atd后台服务托管的,从任务入队、等待触发到执行的全流程都和当前脚本的进程树没有关系,根本不是当前Shell的子进程,wait自然捕捉不到它的运行状态,等再久也没用。
可落地的实现方案
方案1:抓取At任务ID轮询队列状态
atq命令可以列出所有还在队列里的At任务(包含等待时间触发的排队任务、正在执行的任务),我们只要在提交任务时把对应任务的ID记下来,轮询直到这个ID从队列里消失,就说明任务已经完全执行完毕,这时候再跑后续的echo 'hello'就不会有顺序问题。
参考实现:
at_job_id="" if [[ condition ]]; then # 提交At任务,同时从命令回显中提取任务数字ID at_job_id=$(echo "bash ${CommandLine}" | at now + ${Interval} minutes 2>&1 | grep -oP 'job \K[0-9]+') fi # 只有成功提交了At任务才需要执行等待逻辑 if [[ -n "$at_job_id" ]]; then # 每2秒轮询一次,直到目标任务从队列中移除 while atq | awk '{print $1}' | grep -qx "$at_job_id"; do sleep 2 done fi # 后续逻辑按顺序执行 echo 'hello'
注意:如果系统的默认语言不是英文,at命令的回显格式会被本地化翻译,可能导致ID提取失败,这种情况更建议使用方案2。
方案2:用临时文件做完成状态同步
这个方案不依赖at命令的回显格式,兼容性更好。核心思路是拿一个临时文件当同步信号:
- 提交At任务前,先创建一个随机命名的临时文件当标记
- 把要执行的命令拼上“执行完自定义命令后删除这个标记文件”的逻辑
- 当前脚本循环检查这个标记文件是否存在,一旦文件被删掉,就说明At任务已经把自定义命令跑完了,直接跳出循环执行后续逻辑就行
参考实现:
done_flag="" if [[ condition ]]; then # 生成不冲突的临时文件作为状态标记 done_flag=$(mktemp) # 拼接执行逻辑:先跑自定义的CommandLine,执行完后删除标记文件 echo "bash ${CommandLine}; rm -f '${done_flag}'" | at now + ${Interval} minutes fi # 只有创建了标记文件才需要等待 if [[ -n "$done_flag" ]]; then # 每1秒检查一次标记文件状态,直到文件被At任务删除 while [[ -e "$done_flag" ]]; do sleep 1 done fi echo 'hello'
注意:轮询的sleep间隔不要设得太小,一般1-2秒的间隔足够感知任务完成状态,没必要把间隔压到0.1秒级白白浪费CPU资源。
内容的提问来源于stack exchange,提问作者Varduhi
相关产品推荐
相关产品推荐

