最常用但常写错的 nohup 命令必须同时满足忽略 SIGHUP、后台执行、输出重定向、路径与权限可靠四条件;漏一即断开 SSH 后进程大概率消失。

nohup ./script.sh > log 2>&1 & 是最常用但常写错的组合
它不是“加个 nohup 就完事”,而是必须同时满足四个条件:忽略 SIGHUP、后台执行、输出重定向、路径与权限可靠。漏掉任意一个,断开 SSH 后进程大概率消失。
nohup只负责忽略挂断信号,不自动后台运行 —— 必须显式加&- 不重定向输出时,
nohup会尝试写入当前目录下的nohup.out;若目录不可写(比如在/root下普通用户执行),命令直接失败,返回码125 nohup ./script.sh&中nohup和./script.sh之间**必须有空格**,否则 shell 会查找名为nohup./script.sh的命令,报command not found- 脚本内若调用
read、sudo -i、ssh等依赖 TTY 的命令,nohup不提供伪终端,这些命令会立即阻塞或退出
Python 脚本要加 -u 参数,否则日志延迟严重
Python 默认启用 stdout 缓冲,尤其在重定向到文件时,输出可能卡住几十秒甚至更久才刷盘。这不是 nohup 的问题,而是 Python 自身行为。
- 正确写法:
nohup python -u script.py > output.log 2>&1 & - 不加
-u时,tail -f output.log看不到实时日志,容易误判脚本卡死 - 也可在脚本开头加
import sys; sys.stdout.flush()配合手动刷新,但不如-u简单可靠 - 对于使用
logging模块的脚本,需设置stream=sys.stdout并禁用 buffering,或直接用python -u
真正脱离终端控制,用 setsid 而不是 nohup &
当 nohup ./script.sh > log 2>&1 & 意外退出时,很可能是shell在退出前向作业表中的进程发送了SIGHUP信号。即使进程忽略了该信号,某些程序(例如包含readline的交互逻辑)也会主动清理并退出。
setsid ./script.sh > log 2>&1不需要&,它直接 fork 新 session,父进程变为init(PID 1),彻底脱离原终端控制链- 比
nohup更底层,绕过 shell job table,不受disown或exit影响 - 不依赖当前 shell 是否支持 job control,适合嵌入式或最小化环境
- 注意:
setsid不处理输出重定向,必须手动加上> log 2>&1,否则输出仍可能干扰终端
长期服务别硬扛 nohup,改用 systemd --user
nohup 是临时方案,不是服务管理工具。它不处理崩溃重启、依赖顺序、资源限制、日志轮转 —— 这些都得自己补。
- 创建
~/.config/systemd/user/myscript.service,内容包含[Service]段中Type=simple、Restart=on-failure、RestartSec=10 - 启用并启动:
systemctl --user daemon-reload && systemctl --user enable --now myscript.service - 日志统一由
journalctl --user -u myscript.service查看,自动按时间轮转 - 脚本里避免用相对路径 —— systemd 启动时 cwd 是 home 目录,不是脚本所在目录;要么用绝对路径,要么在 service 文件里加
WorkingDirectory=/path/to/script
真正棘手的问题并非“如何启动它”,而是“怎样让它持续稳定地运行”。nohup解决的是信号问题,setsid解决的是会话问题,systemd解决的是生命周期问题——选择哪一个,取决于你是否愿意为稳定性多花费十分钟进行配置。