Linux 后台任务用 disown 让进程不随终端退出
bash disown [选项] [作业号...]
执行命令前,一般先用 `jobs` 命令查看当前 Shell 管理的作业列表。
```bash
jobs -l # -l 会同时显示进程 PID
输出示例:
[1]+ 12345 Running long_running_script.sh &
[2]- 12346 Stopped python train.py
其中 [1]、[2] 就是作业号。+ 表示当前作业,- 表示上一个作业。PID 为 12345 和 12346。
常用操作
-
移除指定作业:
disown %1 # 移除作业 1 disown %2 %3 # 移除作业 2 和 3 disown %python # 移除以 python 开头的命令对应的作业 -
移除所有作业:
disown -a # 移除所有作业 -
移除所有正在运行的作业(不包含已停止的作业):
disown -r -
忽略 SIGHUP 但保留作业列表(作业依然会被记录在 jobs 输出中,只是在退出时不会被杀死):
disown -h %1
注意:没有
-h时,移除后的作业从jobs列表中彻底消失,无法再用fg或bg管理。这在多数场景下是期望的行为,因为程序已经转为守护状态。
实战:从头到尾让程序脱离终端
情景一:程序已在后台运行中
-
启动一个后台任务(或使用Ctrl+Z后bg)
./my_script.sh & -
查看作业
jobs -l # 输出: [1]+ 98765 Running ./my_script.sh & -
使用 disown 移除
disown %1 -
验证
退出当前终端,重新登录,检查进程是否仍在运行:
ps aux | grep my_script.sh你应该能看到 PID 98765 的进程,并且其父进程 ID 变成了 1(或 systemd)。
情景二:程序刚停止,需要先恢复运行再脱离
假设你按下了 Ctrl+Z 暂停了一个任务,并且它尚未被放入后台继续运行:
# 程序运行中被 Ctrl+Z 挂起
[1]+ Stopped ./long_task
# 先用 bg 将其放入后台继续运行
bg %1
# 此时 jobs 会显示 Running
# 再 disown
disown %1
情景三:启动后就忽略,但保留作业控制权
如果你希望程序脱离终端,但又想偶尔用 fg 拉回前台看看(不常见,但可行),可以使用 -h 选项:
./interactive_task &
disown -h %1
这样退出终端后作业不会被杀死,重新登录后虽然无法再 fg(因为新 Shell 没有原有作业列表),但进程仍在运行。如果希望在新终端里接管它,可以借用 reptyr 等工具,但那是进阶话题。
与 nohup 的对比
| 特性 | nohup |
disown |
|---|---|---|
| 使用时机 | 启动命令时 | 启动命令后 |
| 输出处理 | 自动重定向到 nohup.out 文件 |
需手动重定向,否则输出可能丢失或阻塞 |
| 信号免疫 | 直接忽略 SIGHUP | 将作业移除,从而收不到 SIGHUP |
| 作业管理 | 在 jobs 中可见,但退出后结束 |
默认从 jobs 中消失 |
建议:如果你知道某个任务需要长久运行,最好一开始就使用 nohup command > log.txt 2>&1 &,更安全且输出有处可循。disown 是对忘记使用 nohup 或临时决定让任务继续运行时的完美补救方案。
常见问题排查
Q:disown 后,程序的标准输出和错误输出会怎样?
A:如果没有提前重定向,进程可能仍然会试图向已经不存在的终端写入数据,导致收到 SIGPIPE 信号而终止。最稳妥的做法是在启动命令时就进行重定向:
command > output.log 2>&1 &
disown %1