fg 命令详解与参数实测(Ubuntu 22.04)

基于 Ubuntu 22.04(bash 5.1)逐项实测 fg:fg %N 调回前台、无参数取当前作业 %+、%name 按名匹配、Stopped 作业 SIGCONT 唤醒、前台退出码回收,以及 no job control 与 no such job 两类报错形态和 Ctrl-Z 等效模拟。

Linux命令Linux系统UbuntuUbuntu 22.04命令行进程管理2026/10/15

fg 命令详解与参数实测(Ubuntu 22.04)

目标:全面掌握 fg 的每一种用法——把后台作业调回前台、fg %N 按作业号指定、无参数取当前作业(%+)、对 Stopped 作业先 SIGCONT 再调前台、前台作业退出码回收,以及无作业控制与无作业两类报错形态,全部命令与回显已在 Ubuntu 22.04(bash 5.1)实测通过。

一、环境信息

本文所有命令与回显均在以下环境实测采集,读者在相同版本的 Ubuntu 上可直接复现:

项目实测值
操作系统Ubuntu 22.04.5 LTS
架构x86_64
ShellGNU 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 节)。

六、参考链接