fg 命令详解与参数实测(Ubuntu 22.04)
基于 Ubuntu 22.04(bash 5.1)逐项实测 fg:fg %N 调回前台、无参数取当前作业 %+、%name 按名匹配、Stopped 作业 SIGCONT 唤醒、前台退出码回收,以及 no job control 与 no such job 两类报错形态和 Ctrl-Z 等效模拟。
fg 命令详解与参数实测(Ubuntu 22.04)
目标:全面掌握 fg 的每一种用法——把后台作业调回前台、
fg %N按作业号指定、无参数取当前作业(%+)、对 Stopped 作业先 SIGCONT 再调前台、前台作业退出码回收,以及无作业控制与无作业两类报错形态,全部命令与回显已在 Ubuntu 22.04(bash 5.1)实测通过。
一、环境信息
本文所有命令与回显均在以下环境实测采集,读者在相同版本的 Ubuntu 上可直接复现:
| 项目 | 实测值 |
|---|---|
| 操作系统 | Ubuntu 22.04.5 LTS |
| 架构 | x86_64 |
| Shell | GNU bash,版本 5.1.16(1)-release |
| fg 命令类型 | shell builtin(Shell 内置命令,非外部可执行文件) |
| 实测演示目录 | /root/demo/fg |
确认命令与实测回显如下:
$ 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 fg
fg is a shell builtin
💡 说明:fg 是 bash 内置命令,只能由 shell 自身执行(它要操纵的是 shell 的作业表与终端前台进程组),没有独立的
fg可执行文件,也没有--version类选项;版本信息即 bash 版本(见 2.3 节)。
二、命令概览
2.1 语法与测试数据
本节给出 fg 的语法形态,并准备演示目录。fg 的语法只有一种形态:
fg [job_spec]
用法速览:
| 用法 | 作用 | 实测结论 |
|---|---|---|
fg %N | 把作业号为 N 的后台作业调到前台 | 输出该作业命令行,前台等待其结束 |
fg(无参数) | 取「当前作业」(%+,带 + 标记者)调前台 | 等价 fg %+ |
fg %+ / fg %% | 显式引用当前作业 | 与无参数形态等价 |
fg %- | 取「上一个作业」(带 - 标记者)调前台 | 作业已结束时报 no such job |
fg %name | 按命令名(前缀)匹配作业调前台 | fg %sleep 成功调回 sleep 作业 |
fg %不存在 | 引用不存在的作业号 | 报 no such job,rc=1 |
| 对 Stopped 作业 | 先发 SIGCONT 恢复再调前台 | Ctrl-Z 暂停后调回的标准路径 |
关键差异:fg 的全部用法都围绕「选哪个作业」展开——按号(%N)最明确,按名(%name)最直观但可能歧义,无参数默认取 %+ 最省事;指定了已结束或不存在的作业则统一报 no such job 一类错误(rc=1)。
准备演示目录:
$ mkdir -p /root/demo/fg && cd /root/demo/fg && pwd
/root/demo/fg
💡 技术拓展:一个作业在 shell 会话中只有三种执行形态,fg/bg 就是在这三种形态之间搬运作业的开关——
形态 占用终端 启动/切换方式 典型场景 前台(Foreground) 占用,接收键盘输入 直接运行命令;或 fg %N调回需要交互、看实时输出 后台(Background) 不占用 cmd &启动;或 Ctrl-Z 后bg长任务放后台继续用终端 暂停(Stopped) 不占用,也不运行 Ctrl-Z;或脚本中 kill -STOP %N临时挂起,稍后 fg 恢复或 bg 转后台 本文主角 fg 负责「后台/暂停 → 前台」方向的切换;反方向见同专栏《bg 命令详解与参数实测(Ubuntu 22.04)》篇与《jobs 命令详解与参数实测(Ubuntu 22.04)》篇。
2.2 help 全量输出
fg 是内置命令,帮助信息通过 help fg 获取。以下为实测的完整输出(逐行照抄):
$ help fg
fg: fg [job_spec]
Move job to the foreground.
Place the job identified by JOB_SPEC in the foreground, making it the
current job. If JOB_SPEC is not present, the shell's notion of the
current job is used.
Exit Status:
Status of command placed in foreground, or failure if an error occurs.
输出解读:fg 只接受一个可选的 JOB_SPEC;不带参数时使用 shell 认定的「当前作业」。退出码规则是重点——返回被调前台命令的退出状态(出错时才返回失败),这使 fg 成为脚本中回收后台作业退出码的通道(4.2 节实测)。
2.3 版本信息
fg 无独立版本号,其能力由 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
三、参数逐项实测
本章按「无作业控制反例 → 基础调回 → 无参数/%- 引用 → 退出码回收 → Stopped 恢复 → 交互暂停等效模拟」的顺序逐项实测,每项均附真实回显。
3.1 前置反例:无作业控制时 fg 直接报错
先看最常见的踩坑形态:在脚本等非交互 shell 中(作业控制默认关闭),fg 完全不可用。先确认选项串(无 m 即无作业控制),再实测:
$ echo "当前 \$- 选项串: $-"
当前 $- 选项串: hB
即便作业表里有活着的后台作业,fg 也一律报错(为贴近读者终端形态,此处用 bash -c 载体实测,报错前缀为 bash: line 1:;脚本文件内执行时前缀为脚本名与行号,交互终端则为 bash:):
$ bash -c "sleep 60 & jobs; fg %1; true"
[1]+ Running sleep 60 &
bash: line 1: fg: no job control
无作业、无作业控制时同样报错且 rc=1:
$ bash -c "fg; echo rc=\$?"
bash: line 1: fg: no job control
rc=1
💡 要点:
no job control不是「没有作业」,而是「当前 shell 没开作业控制」。脚本里要用 fg,开头加set -m(monitor mode);交互式终端天然可用,无需设置。本篇后续实测均在set -m下进行。
3.2 基础用法:fg %N 把后台作业调回前台
开启作业控制后,启动一个 2 秒的后台作业并用 fg %1 调回前台:
$ set -m
$ sleep 2 &
$ fg %1
sleep 2
$ jobs; echo "rc=$?"
rc=0
输出解读:fg 执行时先打印被调前台的命令行 sleep 2,然后 shell 停在那里等待——这就是「前台」的含义;2 秒后作业正常结束、fg 返回。注意最后的 jobs 输出为空:由 fg 调回前台并执行完毕的作业会立即从作业表移除,而不是像后台自然结束的作业那样以 Done 状态留表等待通知(对照组实测见《jobs》篇 3.8 节 Done 段落)。
3.3 无参数 fg:取当前作业(%+)
不写作业号时,fg 取「当前作业」(jobs 输出中带 + 标记者)。启动两个后台作业,先看作业表确认谁带 +:
$ sleep 2 & sleep 2 &
$ jobs -l
[1]- 435 Running sleep 2 &
[2]+ 436 Running sleep 2 &
$ fg; echo rc=$?
sleep 2
rc=0
输出解读:[2]+ 是当前作业(最后启动),无参数 fg 调走的正是它(PID 436);等价写法是 fg %+。第一个作业 [1]- 在 fg 等待 [2] 的 2 秒里已自然结束,此时再引用它(%-)会得到明确的报错。为贴近读者终端形态,此处用 bash -c 载体完整复现(报错前缀为 bash: line 1:;脚本文件内执行时前缀为脚本名与行号,交互终端则为 bash:):
$ bash -c 'set -m; sleep 2 & sleep 2 & fg; fg %-; echo rc=$?; jobs'
sleep 2
[1]- Done sleep 2
bash: line 1: fg: %-: no such job
rc=1
输出解读:无参数 fg 调走当前作业 [2];等待期间 [1] 自然结束,bash 在下一条命令前报告了 [1]- Done 并将其移出作业表;随后的 fg %- 找不到目标,报 no such job(rc=1)。在另一种时序下(Done 作业尚未被 shell 通知、仍留在表里时),同样的引用会报 fg: job has terminated——两种报错都表示「目标作业已结束,无法调回」,只是作业状态通知时机不同(详见 5.3 节)。
💡 要点:
fg、fg %+、fg %%三者等价,都取当前作业;连续两次裸fg不会依次调回两个作业——第一次执行完后「当前作业」已空或已变,需重新查看jobs确认。
补充三种常用引用形态的实测(为贴近读者终端形态,此处用 bash -c 载体实测,报错前缀为 bash: line 1:)。按命令名匹配调前台:
$ bash -c 'set -m; sleep 2 & fg %sleep; echo rc1=$?'
sleep 2
rc1=0
%% 显式引用当前作业,与无参数等价:
$ bash -c 'set -m; sleep 2 & fg %%; echo rc2=$?'
sleep 2
rc2=0
引用不存在的作业号,报错且 rc=1:
$ bash -c 'set -m; fg %3; echo rc3=$?'
bash: line 1: fg: %3: no such job
rc3=1
fg 的 job_spec 语法与 jobs 完全一致(%N、%%/%+、%-、%name、%?name),更细的匹配规则与歧义报错见《jobs 命令详解与参数实测(Ubuntu 22.04)》篇 3.7 节。
3.4 fg + timeout:验证返回的是前台命令的退出码
help 输出说「退出码 = 被调前台命令的退出状态」,用 timeout 实证:timeout 3 秒后终止 sleep 100(timeout 的退出码约定为 124):
$ timeout 3 sleep 100 &
$ fg %+; echo rc=$?
timeout 3 sleep 100
rc=124
rc=124 正是 timeout 的退出码,穿透 fg 原样返回。由此,脚本里「后台启动 → 调回前台 → 收退出码」的链路成立。
3.5 对 Stopped 作业:SIGCONT 恢复并调前台
fg 遇到暂停(Stopped)状态的作业时,先自动发送 SIGCONT 使其恢复运行,再调到前台。先制造一个 Stopped 作业(交互式终端按 Ctrl-Z 的效果与此等效,脚本中以 kill -STOP 实现):
$ timeout 3 sleep 100 & kill -STOP %+
$ jobs -l
[1]+ 439 Stopped (signal) timeout 3 sleep 100
$ fg %+; echo rc=$?
timeout 3 sleep 100
rc=124
$ jobs; echo "rc=$?"
rc=0
输出解读:Stopped (signal) 明确标注暂停由信号导致;fg %+ 后作业被 SIGCONT 唤醒、回到前台继续执行剩余 3 秒,最终 timeout 生效返回 124。整个「暂停 → 调回 → 跑完 → 收码」闭环成立。
💡 要点:交互终端里 Ctrl-Z 暂停的作业,
fg是「接着跑」,bg是「转后台接着跑」;fg 对 Stopped 作业的隐式 SIGCONT 是这个体验的实现基础。
3.6 无作业时 fg 报错
作业表为空(或当前作业不存在)时,无参数 fg 报错、rc=1(为贴近读者终端形态,此处用 bash -c 载体实测):
$ bash -c 'set -m; fg; echo rc=$?'
bash: line 1: fg: current: no such job
rc=1
对照 3.3 节:作业表还有已结束作业残留时,引用它报的是 job has terminated / %-: no such job;表彻底为空时报 current: no such job。几个报错分别对应「目标已死但还知道是谁」与「根本无目标」两种情形。
3.7 交互式 Ctrl-Z 暂停场景的等效模拟
交互式终端中 Ctrl-Z 的动作是「向前台进程组发送 SIGTSTP」,本环境无法自动化按键,用 kill -STOP 向作业发送 SIGSTOP 等效模拟(两者使进程进入的暂停态一致,jobs 中均显示 Stopped):
$ sleep 300 & kill -STOP %+
$ sleep 0.3; jobs -l
[1]+ 441 Stopped (signal) sleep 300
恢复运行后再终止,完整收尾:
$ kill -CONT %+; sleep 0.3; jobs
[1]+ Running sleep 300 &
$ kill %+; sleep 0.3; jobs
[1]+ Terminated sleep 300
⚠️ 注意:kill 发出信号后作业状态的刷新有短暂延迟,紧贴着查询可能拿到旧状态(如仍显示 Running);示例中的
sleep 0.3即为稳定回显而加。
四、实战示例
本章给出 4 个以 fg 为核心的组合场景,全部为实测原样回显。
4.1 挂起的长任务调回前台查看
后台跑一个持续输出的任务(如 tail -f app.log &),想看实时输出时用 fg 调回——这是交互终端里 fg 最高频的用法。以 sleep 2 模拟「短任务」,完整走一遍后台 → 前台(回显同 3.2 节实测):
$ sleep 2 &
$ fg %1
sleep 2
完整的交互循环是三步往复:cmd & 后台启动 → fg %N 调回前台看输出 → Ctrl-Z 再次挂起。挂起后可 fg %+ 接着看、也可 bg %+ 转后台继续跑(三态总览见 2.1 节拓展表)。注意 fg 调回期间终端被该作业独占,键盘输入直达前台进程;若只想「瞄一眼状态」而不接管终端,用 jobs 查看(见《jobs 命令详解与参数实测(Ubuntu 22.04)》篇)比 fg 更合适。
4.2 脚本中回收后台任务退出码
利用 fg 的退出码穿透(2.2 节 help 的 Exit Status 规则),在脚本中先 & 并行、再 fg 串行收码,替代 wait 的另一种写法:
$ timeout 3 sleep 100 &
$ fg %+; echo rc=$?
timeout 3 sleep 100
rc=124
与 wait 的选型对比(wait 的同款实测:timeout 3 sleep 100 & wait $!; echo "wait rc=$?" → wait rc=124):
| 方式 | 是否切换执行形态 | 终端占用 | 典型场景 |
|---|---|---|---|
fg %+ | 是(后台→前台) | 被作业占用 | 要看实时输出并收退出码 |
wait $! | 否(原地等待) | 空闲 | 脚本中并行后统一收码 |
要点:fg 会把作业真正调回前台执行(终端被占用),wait 只等待不切换;需要「等待且不占终端」用 wait,需要「切换形态并收码」用 fg。
4.3 多作业时的引用顺序演练
两个后台作业并存时,fg(取 %+)与 fg %-(取 %-)的指向由 jobs 的 +/− 标记决定,先 jobs -l 看标记再操作:
$ sleep 2 & sleep 2 &
$ jobs -l
[1]- 435 Running sleep 2 &
[2]+ 436 Running sleep 2 &
$ fg; echo rc=$?
sleep 2
rc=0
被调走的是 [2]+;若想调 [1],直接 fg %1 最明确,不依赖标记记忆。若作业同名无法用 %name 消歧,用作业号 %N 是唯一可靠选择(同名歧义报错的实测见《jobs 命令详解与参数实测(Ubuntu 22.04)》篇 3.7 节 %sleep 段落)。
4.4 暂停作业的唤醒与收尾
对 Stopped 作业执行 fg = SIGCONT + 调前台,适合「刚才 Ctrl-Z 挂起、现在想接着盯完」的场景,完整闭环实测见 3.5 节(timeout 3 sleep 100 暂停后唤醒,rc=124 收尾)。操作口诀:jobs -l 确认 Stopped → fg %+ 唤醒调前台 → 前台结束自动退出;若想转后台继续跑,把 fg 换成 bg 即可(见《bg 命令详解与参数实测(Ubuntu 22.04)》篇)。
五、注意事项与常见问题
5.1 脚本里必须 set -m(no job control 踩坑)
Q1:为什么我在脚本里用 fg 报 no job control?
非交互 shell 默认关闭作业控制(echo $- 中没有 m,实测为 hB),fg 作为作业控制的核心命令直接不可用。解法二选一:脚本开头加 set -m;或改在交互式终端操作。完整对照实测见 3.1 节。注意脚本中即使作业表有作业(jobs 能看到),fg 依旧报错——「有作业表」与「作业控制开启」是两回事。
5.2 fg 与作业表的生命周期
Q2:fg 执行完后为什么 jobs 里看不到这个作业了?
fg 调回前台的作业执行完毕后立即从作业表移除(3.2 节实测 jobs 输出为空);而后台自然结束的作业以 Done 状态留在表里,直到下一次状态通知。两者机制不同,写依赖 jobs 输出的脚本时要区分。
Q3:fg 报 job has terminated 是什么意思?
目标作业已经结束(Done),无法再调回前台(3.3 节实测)。多作业场景中,fg 等待当前作业期间其他作业可能自然结束,引用前先 jobs 确认状态。
5.3 信号与状态刷新
Q4:kill -STOP %+ 之后 jobs -l 还显示 Running?
信号发出到 shell 收割 SIGCHLD、刷新作业状态之间存在毫秒级窗口(3.7 节实测借助 sleep 0.3 稳定显示 Stopped (signal))。脚本化时在信号与查询之间加短暂缓冲。
Q5:Ctrl-Z 和 kill -STOP 一样吗? 效果等价(进程进入暂停态,jobs 显示 Stopped),机制有别:Ctrl-Z 发送 SIGTSTP(可被进程捕获),SIGSTOP 不可捕获、不可忽略。模拟实测见 3.7 节。
Q6:fg 能一次调多个作业吗?
不能。help fg 的语法是 fg [job_spec],仅接受单个作业号(对比 bg 的 [job_spec ...] 复数形态)。
Q7:fg 调回前台的作业,结束之后还找得到吗?
找不到。fg 前台执行完的作业立即从作业表移除(3.2 节实测),引用它会得到 no such job 一类报错。若需要「跑完后还能查看」的后台作业,保持 cmd & 后台形态即可(Done 状态会留表直到被通知,见《jobs 命令详解与参数实测(Ubuntu 22.04)》篇 3.8 节)。
六、参考链接
- bash 手册(Bash Reference Manual,Job Control 章节):https://www.gnu.org/software/bash/manual/html_node/Job-Control.html
- bash 内置命令 fg 官方文档:https://www.gnu.org/software/bash/manual/html_node/Job-Control-Builtins.html
- POSIX Shell Command Language(fg 规范):https://pubs.opengroup.org/onlinepubs/9699919799/utilities/fg.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