0

bash jobs命令管理后台任务生命周期:3个实战技巧让你的任务稳如泰山

2026.08.04 | youres | 68次围观

在 Linux 和 macOS 上写 Shell 脚本时,后台任务管理是个绕不开的话题。启动一个后台进程之后,它到底在跑还是在等?关了终端会不会丢?想把任务拉回前台怎么做?这些问题的答案,全靠 bash jobs 命令和相关工具链撑起来。

这篇文章把后台任务的整个生命周期讲清楚:从启动到查看、从挂起到恢复、从转后台到彻底脱离终端,每一步都有实战代码。搞懂这些,你写出来的脚本在生产环境里才不会莫名其妙断掉。

什么是 bash 后台任务?

默认情况下,你在终端执行的命令是前台任务——终端被它独占,任务不结束你就没法输入新命令。给命令末尾加个 &,它就变成后台任务,终端立即返回让你继续做别的事。

# 启动一个后台睡眠任务 sleep 300 & # 输出类似:[1] 12345 # [1] 是任务编号,12345 是进程ID(PID)

但是注意:标准后台任务(& 启动的)仍然绑定着当前终端。如果终端关闭,这些任务也会收到 SIGHUP 信号然后退出。要让任务真正独立于终端,需要用到后面要讲的 nohupdisown

jobs 命令:查看当前 Shell 的所有后台任务

jobs 是 bash 内建命令,用来列出当前 Shell 会话中所有后台任务的状态。不带参数默认显示任务编号和基本信息,加上不同的选项可以看更多细节。

基础用法:jobs

# 列出所有后台任务 jobs # 输出示例: # [1]- Running sleep 300 & # [2]+ Stopped vim config.yml

输出里每列的含义:

  • [1][2]:任务编号,fgbgkill 命令里用 %1%2 引用它们
  • +(加号):默认操作对象,下一个 fgbg 不带参数时会操作它
  • -(减号):次默认对象,仅当 + 的任务被操作后才升为 +
  • Running:任务正在执行
  • Stopped:任务被 Ctrl+Z 挂起暂停
  • Done:任务已结束(可能还没被 Shell 回收)

jobs 常用选项

jobs -l # 显示 PID(进程ID) jobs -p # 只显示 PID,每行一个,用于脚本中抓取进程号 jobs -r # 只显示正在运行(Running)的任务 jobs -s # 只显示已停止(Stopped)的任务 jobs -x # 将任务编号转换为对应的 PID

下面是一个实际脚本中用 jobs -p 收集所有后台进程 PID 的例子:

#!/usr/bin/env bash # 启动3个后台任务 for i in 1 2 3; do sleep 600 & done # 收集所有后台任务的 PID PIDS=$(jobs -p) echo "后台任务 PID 列表:$PIDS" # 遍历杀掉所有后台任务(清理脚本常用) for pid in $PIDS; do kill "$pid" 2>/dev/null echo "已发送 SIGTERM 到 $pid" done

任务状态转换:Running、Stopped、Done

一个后台任务的完整生命周期会经历三种主要状态:

Running → Stopped(挂起)

前台任务运行中按 Ctrl+Z,任务立即暂停,Shell 收到 SIGTSTP 信号并挂起任务,同时返回一个任务编号。

# 前台运行一个命令 find / -name "*.log" # 按 Ctrl+Z → 输出:[1]+ Stopped find / -name "*.log"

Stopped → Running(恢复)

挂起的任务可以用 bg 继续在后台运行,或者用 fg 拉回前台:

bg # 不带参数,恢复 %+(带+号的任务)到后台继续运行 bg %1 # 恢复任务编号1到后台 fg %2 # 拉任务编号2到前台运行

Done(结束)

任务执行完毕后状态变为 Done。此时任务信息还在 Shell 的作业表中,直到 Shell 处理下一个命令时才会被清理。可以用 wait 命令显式等待某个后台任务结束:

# 启动后台任务并等待其结束 sleep 10 & wait %1 echo "任务完成,退出码:$?"

fg 和 bg:前后台任务切换

fg:拉回前台

fg 把后台或挂起任务拉回前台执行,终端的控制权归它,直到它结束或再次被挂起:

fg # 拉回 %+(默认任务) fg %1 # 拉回任务编号1 fg %% # 同 fg,% 表示当前(带+号)的任务 fg %- # 拉回前一个当前(带-号)的任务

bg:继续在后台运行

bg 把挂起(Stopped)的任务放到后台继续运行。注意:已经 Running 的后台任务不需要 bg

bg # 继续 %+ 到后台 bg %3 # 继续任务编号3到后台

jobs 与 fg/bg 配合的实战场景

一个典型的批量下载场景:

#!/usr/bin/env bash # 同时启动4个下载任务,全部放后台 for url in "${URLS[@]}"; do curl -L -o "${url##*/}" "$url" & done echo "任务已全部启动,当前后台任务:" jobs -l # 等待全部完成 wait echo "所有下载完成!"

disown:彻底脱离 Shell 的任务管理

jobs 列出来的任务都是 Shell 的"亲孩子"——Shell 退出时会向它们发送 SIGHUP,导致任务退出。disown 的作用是从 Shell 的作业表中把任务移出去,这样关闭终端也不会影响它。

disown %1 # 将任务1从作业表移除(任务继续运行) disown # 移除 %+(默认任务) disown -h %2 # 给任务2设置"忽略 SIGHUP",但不从作业表移除 disown -a # 移除所有任务 disown -r # 移除所有正在运行的任务

实战中更推荐用 nohup 配合 & 启动任务,这样任务从一开始就对 SIGHUP 免疫:

# nohup 保证任务不受 SIGHUP 影响 nohup ./long_running_script.sh > output.log 2>&1 & # nohup 后任务就不再受 Shell 作业表管理了 jobs # 看不到 nohup 启动的任务,但 PID 还在运行

监控后台任务状态:自动化脚本写法

在自动化脚本里管理大量后台任务时,经常需要知道哪些还在跑、哪些已经挂了。下面是一个完整的任务池监控模板:

#!/usr/bin/env bash set -m # 开启任务控制(monitor mode),允许后台任务管理 declare -a PIDS # 启动任务池 for i in {1..5}; do (sleep $((RANDOM % 10 + 5)); echo "Task $i done") & PIDS+=($!) done # 轮询检查任务状态 while true; do running=0 for pid in "${PIDS[@]}"; do if kill -0 "$pid" 2>/dev/null; then ((running++)) fi done echo "剩余运行中任务数:$running" if ((running == 0)); then echo "所有任务已完成" break fi sleep 2 done

这个脚本用 kill -0 检测进程是否存在,比 jobs -r 更可靠——因为 jobs 只看当前 Shell 的作业表,而 kill -0 直接问内核这个 PID 还在不在。

总结

bash jobs 命令是整个后台任务管理体系的入口,配合 fgbgdisownnohup 四个工具,你可以完整掌控一个任务的生老病死:

  • & 启动后台任务,用 jobs -l 查看状态
  • Ctrl+Z 挂起前台任务,用 bg 继续后台运行
  • fg 把任务拉回前台交互操作
  • nohupdisown 让任务脱离终端生命周期
  • wait 等待任务完成并获取退出码

把这些搞清楚之后,写长期运行的自动化脚本心里就有底了——你知道任务在哪、状态如何、不受终端关闭影响。


相关阅读:
wait -n实现Shell并发数控制任务池:3种写法让批量任务跑满不超载
bash wait -n和wait区别完整对比:并行脚本效率提升的关键区别
xargs并行处理任务中断恢复方法:3个实战技巧让你的批量任务不怕被中断
版权声明

本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

发表评论