Linux 后台任务用 disown 让进程不随终端退出

FreeGuideOnline 最新 2026-07-05

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 列表中彻底消失,无法再用 fgbg 管理。这在多数场景下是期望的行为,因为程序已经转为守护状态。

实战:从头到尾让程序脱离终端

情景一:程序已在后台运行中

  1. 启动一个后台任务(或使用Ctrl+Z后bg)

    ./my_script.sh &
    
  2. 查看作业

    jobs -l
    # 输出: [1]+ 98765 Running    ./my_script.sh &
    
  3. 使用 disown 移除

    disown %1
    
  4. 验证

    退出当前终端,重新登录,检查进程是否仍在运行:

    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