ssh 退出后想让命令留在后台跑,最简单的办法是 nohup ./import.sh > import.log 2>&1 &——这适用于你提前就知道这个进程会活得比会话久的情况。但还有五种这条命令管不了的情况:比如启动之后才想起来忘了加 nohup,或者进程不光要扛住 ssh 断开,还得扛住重启。
1. 提前知道这个进程会跑很久
$ clai 运行这个脚本,让它扛得住 ssh 断开,并把输出写到文件里→ nohup ./import.sh > import.log 2>&1 &
nohup 拦截了终端关闭时进程会收到的 SIGHUP 信号。& 把它丢进后台。重定向不是可选项:不加的话,输出会跑到当前目录下的 nohup.out 里,之后你还得去找。
2. 已经启动了,忘了加 nohup
$ clai 把我已经启动的进程和终端脱钩,我忘了加 nohup→ Ctrl+Z; bg; disown -h %1
Ctrl+Z 挂起它,bg 让它在后台继续跑,disown -h 把它从 shell 的任务表里删掉,这样就不会再收到 SIGHUP。这是进程已经跑了一小时、又舍不得杀掉时的补救办法。
3. 需要彻底和会话脱钩
$ clai 在独立会话里运行这个命令,彻底和终端脱钩→ setsid --fork ./import.sh > import.log 2>&1
setsid 会创建一个新的会话和进程组,所以进程根本收不到终端信号。比 nohup 更彻底:它一开始就没有可以失去的控制终端。
4. 之后还要回来看一眼
$ clai 在后台运行,这样我之后能重新连上去看看→ tmux new -d -s import './import.sh'
-d 会以脱离状态创建会话。之后用 tmux attach -t import 就能直接回到进程运行的活终端里。这是唯一一个不光能看,还能交互的选项。
5. 让 systemd 来看管
$ clai 把这个跑成一个临时的 systemd 服务,要有日志和状态→ systemd-run --user --unit=import ./import.shRunning as unit: import.service; invocation ID: 74282bc256f144cdb323c19fc09452af
日志用 journalctl --user -u import 看,状态用 systemctl --user status import 查,需要的话还能自动重启。对那种既要扛住你退出、又要扛住重启的任务来说,这是最好的选择。
6. 确认进程是不是真的脱钩了
$ clai 检查这个进程是不是真的和终端脱钩了→ ps -o pid,ppid,sid,tty,comm -p 12345
如果 TTY 列显示 ?,就说明没有控制终端,断开连接伤不到它。如果显示的是 pts/0,它还挂在你的会话上,会话没了它也就没了。
坑
nohup不加重定向的话,输出会写进 nohup.out。 这个文件建在当前目录,如果当前目录不能写就建在家目录。最好显式指定路径。disown挡不住已经发出去的 SIGHUP。 它只是把任务从 shell 的记录里去掉。如果终端已经关了,那就晚了。- tmux 和 screen 扛不住重启。 需要扛住重启的任务,得用 systemd 单元,不能靠多路复用器。
相关问题
该用 nohup 还是 tmux? nohup 是"启动就不管了",输出写文件。tmux 则是你还得回到一个活的进程里,给它敲点东西的时候用。
为什么退出之后进程还是死了? 要么它收到的根本不是 SIGHUP——是跟着父进程一起没的——要么就是自己跑挂了。去看日志,前提是你重定向过。
怎么看一个已经在后台跑的进程的输出? 如果输出写到了文件里,用 tail -f。如果哪儿都没写,可以用 strace -p PID -e write 或者 /proc/PID/fd 里的描述符偷看一下。
另请参阅
CliAI 不会替你记得加 nohup,但会在你运行之前就选对工具。一行命令安装。