bg 命令详解与参数实测(Ubuntu 22.04)
基于 Ubuntu 22.04(bash 5.1)逐项实测 bg:Stopped 作业恢复为后台运行、无参数取当前作业 %+、bg %1 %2 批量恢复、与 & 直接后台的等效性、already in background 与 no job control 报错形态,以及 bg 后接 disown 防断连收尾。
bg 命令详解与参数实测(Ubuntu 22.04)
目标:全面掌握 bg 的每一种用法——把 Stopped 作业恢复为后台运行、
bg %N按作业号指定、无参数取当前作业(%+)、批量恢复多个作业、与&直接后台的等效关系,以及对 Running 作业执行 bg 的报错处理,全部命令与回显已在 Ubuntu 22.04(bash 5.1)实测通过。
一、环境信息
本文所有命令与回显均在以下环境实测采集,读者在相同版本的 Ubuntu 上可直接复现:
| 项目 | 实测值 |
|---|---|
| 操作系统 | Ubuntu 22.04.5 LTS |
| 架构 | x86_64 |
| Shell | GNU bash,版本 5.1.16(1)-release |
| bg 命令类型 | shell builtin(Shell 内置命令,非外部可执行文件) |
| 实测演示目录 | /root/demo/bg |
确认命令与实测回显如下:
$ head -2 /etc/os-release
PRETTY_NAME="Ubuntu 22.04.5 LTS"
NAME="Ubuntu"
$ uname -m
x86_64
$ echo "BASH_VERSION=$BASH_VERSION"
BASH_VERSION=5.1.16(1)-release
$ type bg
bg is a shell builtin
💡 说明:bg 是 bash 内置命令,只能由 shell 自身执行(它要操纵的是 shell 的作业表并向作业进程组发送 SIGCONT),没有独立的
bg可执行文件,也没有--version类选项;版本信息即 bash 版本(见 2.3 节)。
二、命令概览
2.1 语法与测试数据
本节给出 bg 的语法形态,并准备演示目录。bg 的语法:
bg [job_spec ...]
用法速览:
| 用法 | 作用 | 实测结论 |
|---|---|---|
bg %N | 把作业号为 N 的 Stopped 作业恢复为后台运行 | 输出 [N]+ cmd &,状态转 Running |
bg(无参数) | 取「当前作业」(%+,带 + 标记者) | 等价 bg %+ |
bg %1 %2 | 批量恢复多个 Stopped 作业 | 支持多作业号,逐个恢复 |
| 对 Running 作业 | 目标已在后台运行 | 报 job N already in background(rc=0) |
| 对不存在的作业 | 引用无效作业号 | 报 no such job(rc=1) |
| 无作业控制 | 非交互 shell 默认形态 | 报 no job control(rc=1) |
准备演示目录:
$ mkdir -p /root/demo/bg && cd /root/demo/bg && pwd
/root/demo/bg
关键差异:bg 的定位是「恢复已被暂停的作业」,它与 & 的分工在于时机——& 是启动时就放后台,bg 是启动后(被 Ctrl-Z 或 SIGSTOP 暂停)再转回后台;对本来就 Running 的后台作业执行 bg 没有意义,bash 会给出 already in background 提示(3.2 节实测)。
💡 技术拓展:让程序「不占用终端继续跑」的方式不止 bg 一种,按「是否依赖当前会话」横向对比——
方式 原理 会话断开后 典型场景 cmd &启动即放后台,仍属当前会话作业表 可能收到 SIGHUP 被终止 短时后台任务 Ctrl-Z → bg前台暂停后转后台(bg 的标准路径) 同上 跑到一半才发现要腾出终端 nohup cmd &忽略 SIGHUP,输出写入 nohup.out 存活 断网/关终端跑长任务 disown %N事后把作业从作业表摘除 存活 作业已启动才想起要防断连 setsid cmd &新会话新进程组,彻底脱离终端 存活 守护化启动 本文主角 bg 服务于「当前会话内前台/后台的调度」;防断连场景见上表后两行,nohup 的完整用法见同专栏《nohup 命令详解与参数实测(Ubuntu 22.04)》篇。
2.2 help 全量输出
bg 是内置命令,帮助信息通过 help bg 获取。以下为实测的完整输出(逐行照抄):
$ help bg
bg: bg [job_spec ...]
Move jobs to the background.
Place the jobs identified by each JOB_SPEC in the background, as if they
had been started with `&'. If JOB_SPEC is not present, the shell's notion
of the current job is used.
Exit Status:
Returns success unless job control is not enabled or an error occurs.
输出解读:bg 接受多个 JOB_SPEC(复数形态,对比 fg 的单数),语义是「如同用 & 启动一样」把作业放到后台;不带参数时取「当前作业」。退出码:除非作业控制未开启或出错,否则返回成功——注意「目标已在后台运行」的 already in background 不算 error,rc 仍为 0(3.2 节实测)。
2.3 版本信息
bg 无独立版本号,其能力由 bash 决定。实测版本确认:
$ bash --version | head -1
GNU bash, version 5.1.16(1)-release (x86_64-pc-linux-gnu)
$ echo $BASH_VERSION
5.1.16(1)-release
三、参数逐项实测
本章按「无作业控制反例 → Running 反例 → Stopped 恢复主线 → 批量恢复 → 引用形态 → disown 收尾」的顺序逐项实测,每项均附真实回显。
3.1 前置反例一:无作业控制时 bg 报错
在脚本等非交互 shell 中(作业控制默认关闭),bg 完全不可用。先确认选项串(无 m 即无作业控制):
$ echo "当前 \$- 选项串: $-"
当前 $- 选项串: hB
即便作业表里有活着的后台作业,bg 也一律报错。为贴近读者终端形态,此处用 bash -c 载体实测(报错前缀为 bash: line 1:;脚本文件内执行时前缀为脚本名与行号,交互终端则为 bash:):
$ bash -c 'bg; echo rc=$?'
bash: line 1: bg: no job control
rc=1
💡 要点:
no job control不是「没有作业」,而是「当前 shell 没开作业控制」。脚本里要用 bg,开头加set -m(monitor mode);交互式终端天然可用。本篇后续实测均在set -m下进行。
3.2 前置反例二:对 Running 作业执行 bg
开启作业控制后,一个本来就在后台运行的作业再执行 bg,bash 给出提示性报错(rc=0,不算失败)。先看作业表里的 Running 作业:
$ set -m
$ sleep 300 &
$ jobs -l
[1]+ 462 Running sleep 300 &
执行 bg 触发提示。为贴近读者终端形态,此处用 bash -c 载体实测(报错前缀为 bash: line 1:;脚本文件内执行时前缀为脚本名与行号,交互终端则为 bash:):
$ bash -c 'set -m; sleep 60 & bg %1; echo rc=$?; kill %1'
bash: line 1: bg: job 1 already in background
rc=0
输出解读:报错文本直白——作业已在后台,无需恢复;rc=0 与 help bg 的退出码规则一致(这不算 error)。目标作业状态不受影响,仍是 Running。
💡 要点:看到
already in background先检查目标状态——多半是 Ctrl-Z 没按(作业根本没暂停),或者作业号写错指向了另一个 Running 作业。
3.3 主线用法:bg 把 Stopped 作业恢复为 Running
bg 的标准路径:作业处于 Stopped 状态 → bg 恢复其在后台运行。先制造 Stopped(交互式终端按 Ctrl-Z 的效果与此等效,脚本中以 kill -STOP 实现):
$ sleep 301 & kill -STOP %+
$ jobs -l
[1]+ 464 Stopped (signal) sleep 301
执行 bg %+,作业被 SIGCONT 唤醒并在后台继续运行:
$ bg %+
[1]+ sleep 301 &
$ jobs -l
[1]+ 464 Running sleep 301 &
输出解读:bg 输出 [1]+ sleep 301 &——作业号加命令行加 &,与 & 直接后台启动时的回显一致,返回码为 0;jobs -l 复核状态从 Stopped (signal) 变为 Running,PID 464 不变(同一进程被唤醒而非重启)。验证后清理:
$ kill %+; sleep 0.3; jobs
[1]+ Terminated sleep 301
💡 要点:交互式终端里「Ctrl-Z → bg」是与「Ctrl-Z → fg」并列的两个出口——fg 回前台接着跑,bg 转后台接着跑。bg 发送 SIGCONT 使暂停进程恢复,但不把作业调到前台,终端继续可用。
3.4 & 直接后台与 STOP→bg 的等效性
cmd & 与「暂停后 bg」最终都得到 Running 状态的后台作业。对比实测,先看 & 直接后台:
$ sleep 302 &
$ jobs -l
[1]+ 466 Running sleep 302 &
再看「启动后暂停、再 bg 恢复」:
$ sleep 303 & kill -STOP %+
$ jobs -l
[1]- 466 Running sleep 302 &
[2]+ 467 Stopped (signal) sleep 303
$ bg %+
$ jobs -l
[1]- 466 Running sleep 302 &
[2]+ 467 Running sleep 303 &
两条路径的终点完全一致:[2]+ 467 Running sleep 303 &。区别只在中间多了一步暂停——这正是交互场景的价值:程序已经在前台跑起来才发现要腾出终端,Ctrl-Z(等效 kill -STOP)加 bg 就是补救手段,不需要杀掉重来。清理:
$ kill %+ %-; sleep 0.3; jobs
[1]- Terminated sleep 302
[2]+ Terminated sleep 303
3.5 批量恢复:bg %1 %2
bg 支持一次恢复多个 Stopped 作业。启动两个并全部暂停(用明确的作业号 %1 %2,避免 %+/%- 指针歧义):
$ sleep 304 & sleep 305 &
$ kill -STOP %1 %2
$ sleep 0.3; jobs -s
[1]+ Stopped sleep 304
[2]- Stopped sleep 305
批量恢复并复核:
$ bg %1 %2
[1]+ sleep 304 &
[2]+ sleep 305 &
$ sleep 0.3; jobs -l
[1]- 469 Running sleep 304 &
[2]+ 470 Running sleep 305 &
$ kill %+ %-; sleep 0.3; jobs
[1]- Terminated sleep 304
[2]+ Terminated sleep 305
输出解读:bg %1 %2 逐个恢复并各输出一行 [N]+ cmd &;jobs -l 确认两个作业都回到 Running。注意暂停与恢复之间用 sleep 0.3 留出信号处理窗口,否则可能读到未刷新的旧状态(见 5.3 节)。
💡 要点:批量操作时优先用明确的
%N作业号而非%+/%-——bg 恢复一个作业后,当前/上一作业指针会重排,混合使用相对引用容易出现「第二个参数指向刚恢复的作业」的意外(此时报 already in background)。
3.6 引用形态:无参数取 %+、按名匹配、无效作业号
无参数 bg 等价 bg %+,取当前作业。完整链路(含返回码与复核)实测:
$ set -m; sleep 308 & kill -STOP %+; bg; echo rc=$?; jobs -l; kill %+; sleep 0.3; true
[1]+ sleep 308 &
rc=0
[1]+ 194 Running sleep 308 &
[1]+ Terminated sleep 308
输出解读:无参数 bg 恢复了暂停中的当前作业([1]+ sleep 308 &),rc=0;jobs -l 确认 Running;最后 kill 收尾打印 Terminated。
对不存在的作业号报错(为贴近读者终端形态,此处用 bash -c 载体实测):
$ bash -c 'set -m; bg %3; echo rc=$?'
bash: line 1: bg: %3: no such job
rc=1
按命令名匹配与 fg 一致,bg %name 同样可用(匹配规则与歧义处理详见《jobs 命令详解与参数实测(Ubuntu 22.04)》篇 3.7 节)。
3.7 无作业时 bg 报错
作业表为空(或当前作业不存在)时,无参数 bg 报错、rc=1:
$ bash -c 'set -m; bg; echo rc=$?'
bash: line 1: bg: current: no such job
rc=1
3.8 bg 之后接 disown:脱离作业表
bg 恢复的作业仍是会话内的普通后台作业,会随会话结束收到 SIGHUP;若要防断连,可接 disown 把它从作业表摘除。完整链路:启动 → 暂停 → bg 恢复 → disown:
$ sleep 307 & kill -STOP %+; bg %+; PID307=$!
[1]+ sleep 307 &
$ disown $PID307
$ jobs; echo "rc=$?"
rc=0
作业表已空,但进程还在(STAT 为 S,即 sleep 的正常等待态):
$ ps -o pid,ppid,pgid,stat,cmd -p $PID307
PID PPID PGID STAT CMD
476 451 476 S sleep 307
验证后清理:
$ kill $PID307; sleep 0.3; echo "清理 rc=$?"
清理 rc=0
💡 要点:disown 用
$!(最后启动的后台进程 PID)而非作业号引用——bg 操作不改变$!的指向,这一写法在批量脚本里比作业号更可靠。
四、实战示例
本章给出 4 个以 bg 为核心的组合场景,全部为实测原样回显。
4.1 交互场景:前台跑到一半转后台
交互终端里最经典的补救操作:命令已经在前台执行才发现需要腾出终端,按 Ctrl-Z 暂停(本环境用 kill -STOP 等效模拟)再 bg 恢复,程序无感知地继续跑(完整回显见 3.3 节实测):
$ sleep 301 & kill -STOP %+
$ jobs -l
[1]+ 464 Stopped (signal) sleep 301
$ bg %+
[1]+ sleep 301 &
$ jobs -l
[1]+ 464 Running sleep 301 &
要点:Ctrl-Z 暂停不会终止进程(状态是 Stopped 而非 Terminated),bg 恢复后 PID 不变、进度不丢。
4.2 多任务并行:批量暂停批量恢复
批量后台任务的「全停再全开」流程,配合 jobs -s 过滤 Stopped 状态作业:
$ sleep 304 & sleep 305 &
$ kill -STOP %1 %2
$ sleep 0.3; jobs -s
[1]+ Stopped sleep 304
[2]- Stopped sleep 305
$ bg %1 %2
[1]+ sleep 304 &
[2]+ sleep 305 &
要点:jobs -s 只列 Stopped 作业,天然就是「待恢复清单」;bg 支持的复数 job_spec 让批量恢复一条命令完成。
4.3 & 与 bg 的选型对照
两种后台化路径的完整对照实测见 3.4 节:sleep 302 & 与 sleep 303 & kill -STOP %+ → bg %+ 的终点回显一致(Running ... &)。选型口诀:启动时就知道要后台,直接 &;跑起来才要腾终端,Ctrl-Z 加 bg。两者都是会话内作业,防断连需求再叠加 nohup 或 disown(2.1 节拓展表)。
4.4 bg 恢复后防断连收尾
bg 恢复的长任务若担心会话断开被杀,接 disown 摘除作业表关联,完整链路实测见 3.8 节(bg 后 disown $PID,jobs 清空而 ps 确认进程存活)。若任务尚未启动,更优雅的做法是启动时就用 nohup cmd &(见同专栏《nohup 命令详解与参数实测(Ubuntu 22.04)》篇)。
五、注意事项与常见问题
5.1 脚本里必须 set -m(no job control 踩坑)
Q1:为什么我在脚本里用 bg 报 no job control?
非交互 shell 默认关闭作业控制(echo $- 中没有 m,实测为 hB),bg 作为作业控制的核心命令直接不可用。解法二选一:脚本开头加 set -m;或改在交互式终端操作。实测见 3.1 节。注意脚本中即使作业表有作业(jobs 能看到),bg 依旧报错——「有作业表」与「作业控制开启」是两回事。
5.2 already in background 不是故障
Q2:bg: job 1 already in background 是出错了吗?
是提示不是故障,退出码为 0(3.2 节实测)。它表示目标作业本来就 Running,无需也不存在「恢复」动作。常见原因:忘记先 Ctrl-Z/kill -STOP 暂停,或 %+ 指向了刚被 bg 恢复的作业。真正的错误(无此作业、无作业控制)退出码才是 1。
5.3 信号与状态刷新时序
Q3:kill -STOP %1 之后 jobs 还显示 Running?
信号发出到 shell 收割 SIGCHLD、刷新作业状态之间存在毫秒级窗口。本篇 3.5、3.6 节的 sleep 0.3 缓冲即为稳定回显而加;批量暂停后立即 bg 也可能因状态未刷新而报 already in background——先缓冲再操作。
Q4:Ctrl-Z 和 kill -STOP 一样吗? 效果等价(进程进入暂停态,jobs 显示 Stopped),机制有别:Ctrl-Z 发送 SIGTSTP(可被进程捕获),SIGSTOP 不可捕获、不可忽略。脚本自动化只能用后者。
5.4 与相邻命令的分工
Q5:bg、fg、& 该怎么选?
三者是同一套作业控制机制的不同入口:& 启动即后台,bg 把暂停作业转后台,fg 把后台/暂停作业调前台。三者都依赖作业控制开启(交互终端默认开启,脚本需 set -m),都用同一套 jobspec 语法。
Q6:bg 恢复的作业会随终端关闭死掉吗?
会。bg 只改变「前台/后台」形态,不改变作业与会话的从属关系,会话退出时后台作业可能收到 SIGHUP。需要跨会话存活,用 nohup cmd &、disown %N 或 setsid(横向对比见 2.1 节拓展表)。
Q7:bg 能一次恢复多个作业吗?
能,这是 bg 与 fg 的语法差异之一:bg [job_spec ...] 复数(bg %1 %2 批量恢复实测见 3.5 节),fg [job_spec] 单数。
六、参考链接
- bash 手册(Bash Reference Manual,Job Control 章节):https://www.gnu.org/software/bash/manual/html_node/Job-Control.html
- bash 内置命令 bg 官方文档:https://www.gnu.org/software/bash/manual/html_node/Job-Control-Builtins.html
- POSIX Shell Command Language(bg 规范):https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bg.html
- man7.org Linux man-pages(signal 信号机制):https://man7.org/linux/man-pages/man7/signal.7.html
- Ubuntu 22.04 bash 软件包页:https://packages.ubuntu.com/jammy/bash