跳转到内容

bg / fg

bg、fg、jobs 是 shell 的作业控制内建命令,管的是当前这个终端里跑着的任务:把前台任务丢到后台、把后台任务捞回前台、看看手上有几个。

Terminal window
jobs [-l]
bg [作业号]
fg [作业号]

三个都是 shell 内建命令,which bg 查不到,见 which 作业号只在当前 shell 里有效,换一个终端就不认识了

作业的几种写法:

写法 指代
%1 作业号为 1 的作业,直接写 1 多数 shell 也认
%% 或 %+ 当前作业,bg、fg 不带参数时的默认对象
%- 上一个作业
%字符串 命令以该字符串开头的作业

作业和进程是两个层面的概念:

  • 进程是内核的概念,有全局唯一的 PID
  • 作业是 shell 的概念,只在当前 shell 内有意义。一条管道 a | b | c 是三个进程,但只是一个作业

前台作业占据终端的输入,shell 会等它结束;后台作业不占输入,shell 立刻回到提示符。但输出是另一回事 —— 后台作业默认仍然共用终端的 stdout 和 stderr,所以它打印的东西会插进你正在敲的命令里。

作业状态的切换靠信号完成:

操作 实际发生的事
Ctrl-Z 给前台作业发 TSTP,进程进入 T(已停止)状态
bg 发 CONT 让它继续,但不把它放回前台
fg 发 CONT,同时把终端的控制权交回给它
Ctrl-C 发 INT,通常就是终止

信号的含义见 kill。

命令 作用
Ctrl-Z 暂停当前前台作业
jobs 列出当前 shell 的所有作业
jobs -l 同时显示 PID
bg %1 让暂停的作业 1 在后台继续运行
fg %1 把作业 1 调回前台
command & 启动时就放到后台
disown %1 把作业从 shell 的作业表里摘掉,关终端时不会被 HUP
wait 等所有后台作业结束

1. 忘了加 & 的补救

Terminal window
./long-task

任务已经跑起来并占着终端,按 Ctrl-Z 再:

Terminal window
bg
jobs -l

输出:

[1]+ 20481 Running ./long-task &

2. 在编辑器和 shell 之间来回切

Terminal window
vim notes.md

Ctrl-Z 回到 shell 做点别的,然后:

Terminal window
fg

比另开一个终端快,而且编辑器的状态原样保留

3. 关掉终端也要接着跑

Terminal window
./long-task &
disown %1

或者一开始就想好:

Terminal window
nohup ./long-task > out.log 2>&1 &

disown 是事后补救,nohup 是事前预防,都是为了挡住终端关闭时的 HUP 要长期跑的东西还是放进 tmux 更省心,随时能连回去看

4. 后台作业的输出要重定向掉

Terminal window
./task > task.log 2>&1 &

不重定向的话,后台作业的输出会直接打到终端上,和你正在输入的命令混成一团 重定向的写法见 stdio

5. 后台作业读输入会被停住

Terminal window
jobs

输出:

[1]+ Stopped (tty input) ./interactive

后台作业想从终端读输入时会收到 TTIN 而被暂停 用 fg 把它调回前台,输入完再 Ctrl-Z + bg 送回去