Linux后台运行Python脚本并保存日志的方法
详细讲解在Linux系统中后台持久化运行Python脚本并完整保存标准输出与错误日志的常用方案,涵盖nohup重定向语法拆解、-u禁用输出缓冲、后台进程启停维护以及systemd生产级守护配置。
一、为什么直接后台运行还不够?核心问题与运行原理
很多初学者在执行脚本时习惯在末尾添加一个 & 符号(例如 python3 app.py &)。虽然 & 能够让命令立即让出终端提示符并在后台运行,但该进程依然挂载在当前控制终端(tty / pts)的会话组中。当 SSH 会话断开或终端窗口关闭时,系统会向该会话名下的所有子进程发送 SIGHUP(Hangup)挂起信号,导致后台脚本瞬间被强制终止。
此外,Linux 控制台默认将输出直接打印在屏幕伪终端设备上。一旦失去屏幕终端,未重定向的数据流将无处可去。因此,要实现真正的“免打扰后台运行并留痕”,必须同时满足三个技术条件:
- 屏蔽终止信号:让进程忽略终端退出时发出的
SIGHUP信号; - 解绑交互输入输出:将进程的标准输出(stdout)和标准错误(stderr)转接到磁盘文件;
- 解决 I/O 缓冲延迟:防止 Python 默认的缓冲机制导致日志迟迟不写盘。
二、最通用的基础方案:nohup 结合 I/O 重定向
nohup(no hang up)是 Linux 系统原生自带的实用工具,用于运行不受挂起信号影响的命令。它是单机运行中小型脚本最便捷、最常用的方案。
1. 标准执行命令
# 标准单行启动模板(替换为您自己的脚本名和日志路径)
nohup python3 -u script.py > app.log 2>&1 &
2. 各参数详细拆解
这条经典命令由五个核心部分组合而成,每一个选项都承担着明确的功能职责:
nohup:告知操作系统忽略所有SIGHUP信号。即便远程登录连接因网络波动断开或用户主动输入exit退出,该进程也不会受波及;python3 -u:极其关键的参数。-u(unbuffered)强制 Python 对标准输出和标准错误采用无缓冲模式,每产生一行print就立即同步写到磁盘,解决日志文件长时间空白的顽疾;> app.log:将文件描述符1(标准输出 stdout)重定向到名为app.log的文件。单大于号>表示每次启动覆盖原文件;如果您希望保留历史日志并在末尾追加,请使用双大于号>> app.log;2>&1:将文件描述符2(标准错误 stderr)重定向到描述符1当前指向的位置。这样无论是程序中的正常print输出,还是未捕获的报错堆栈信息(Traceback),都会统一按时间顺序收拢到同一个日志文件中;&:通知 Shell 将该命令作为后台作业执行,回车后 Shell 立即交还控制权,允许在当前终端继续操作。
input() 调用,转入后台后可能因等待终端输入而被系统置于暂停挂起状态(T T 状态)。为彻底防范此问题,可以在命令中显式封闭输入流:nohup python3 -u script.py < /dev/null > app.log 2>&1 &。
三、后台任务的日常维护:查看、确认与停止
命令提交到后台后,并不意味着维护工作的结束。读者需要掌握如何确认脚本正在健康运行、如何动态观察输出,以及如何安全平稳地停止任务。
步骤 1:获取与记录进程 ID(PID)
在执行带 & 的命令后,终端第一行通常会返回两个数字,例如 [1] 28541。其中方括号内的 1 是作业编号(Job ID),后面的 28541 则是系统赋予的进程 ID(PID)。
在编写自动化脚本或部署流程时,推荐利用 Shell 内置变量 $!(代表最后一个置于后台的进程 PID)直接将进程号保存至文件中:
nohup python3 -u script.py > app.log 2>&1 & echo $! > app.pid
步骤 2:验证进程运行状态
脚本启动数秒后,应主动检查进程是否存活,排除因环境缺包或语法错误引发的“启动即闪退”:
# 方式 A:通过 ps 与 grep 查找
ps aux | grep script.py
# 方式 B:通过 pgrep 快速列出对应 PID
pgrep -fl script.py
输出示例(明确标示进程运行中):
user 28541 1.2 0.8 124356 32412 pts/1 S 10:15 0:02 python3 -u script.py
步骤 3:实时动态追踪日志
使用 tail -f 可以持续追踪日志增量,效果与直接在终端前台查看完全一致。按 Ctrl + C 仅退出 tail 监视界面,绝不会影响后台运行中的 Python 脚本。
# 查看末尾 50 行并开启动态滚动监听
tail -n 50 -f app.log
步骤 4:安全终止后台进程
停止正在后台运行的 Python 脚本时,务必优先采用 SIGTERM(信号 15)让程序优雅关闭。只有当程序无响应、处于僵死状态时,才考虑强制发送 SIGKILL(信号 9):
# 优雅终止(推荐:允许 Python 执行 try...finally、关闭数据库连接与保存文件)
kill -15 28541
# 若已记录 app.pid 文件,可直接调用:
kill -15 $(cat app.pid)
# 仅在无响应超时时使用强制终止:
kill -9 28541
四、为什么日志文件经常为空?深入解析 Python 输出缓冲
许多开发者在初次使用 nohup 时常遇到一个困惑:明明程序在后台运行且无报错,但通过 cat app.log 或 tail -f app.log 查看时,日志文件长时间显示为 0 字节,直到脚本全部执行完毕或者强制杀死时,日志才瞬间喷涌而出。如果程序在运行途中意外宕机,最后的崩溃信息将彻底丢失。
背后的原因:TTY 与 Block Buffering
在 Linux 体系中,C 标准 I/O 库和 Python 会自动感知当前输出流的目标类型:
- 行缓冲(Line Buffered):当输出目标是交互式终端(TTY / PTS)时,每次遇到换行符
就会自动刷新屏幕; - 全缓冲(Block Buffered):当输出被重定向到非终端设备(例如普通的磁盘文件
app.log)时,系统为了节省高频磁盘 I/O 开销,默认开启全缓冲模式。输出内容会被暂存在 4KB 或 8KB 大小的内存缓冲区中,只有当缓冲区被填满或程序正常退出时,才会执行实际的写盘操作。
三种针对性的解决办法
要让日志能够实时、逐行出现在文件中,可以根据部署环境选择以下任意一种手段:
- 命令行显式传参(最推荐):在执行解释器时加上
-u,即python3 -u script.py > app.log 2>&1 &; - 配置环境变量:在执行前设定
PYTHONUNBUFFERED=1,例如export PYTHONUNBUFFERED=1或PYTHONUNBUFFERED=1 nohup python3 script.py ...; - 在 Python 代码内主动刷新:修改关键代码中的
print语句,加上刷新参数:print("处理进度", flush=True),或显式调用import sys; sys.stdout.flush()。
五、生产环境高可用方案:使用 systemd 注册为系统守护服务
nohup 适合临时任务和开发测试,但若面对的是生产环境下的核心爬虫、消息监听服务或 Web API,它缺乏“开机自启动”和“异常崩溃自动拉起”的能力。在现代主流 Linux 系统(如 Ubuntu 20.04+、Debian 11+、CentOS 7+、Rocky Linux)中,使用 systemd 是最严谨的工业级方案。
1. 编写 Unit 服务配置文件
以管理员权限在 /etc/systemd/system/ 目录下创建配置文件,例如 my_script.service:
# /etc/systemd/system/my_script.service
[Unit]
Description=Python 自动化后台守护任务
After=network.target
[Service]
Type=simple
User=ubuntu
WorkingDirectory=/home/ubuntu/project
Environment=PYTHONUNBUFFERED=1
# 必须使用绝对路径指定 Python 解释器(支持虚拟环境)与脚本位置
ExecStart=/home/ubuntu/project/venv/bin/python3 -u /home/ubuntu/project/script.py
Restart=always
RestartSec=5s
StandardOutput=append:/home/ubuntu/project/logs/app.log
StandardError=append:/home/ubuntu/project/logs/app.log
[Install]
WantedBy=multi-user.target
2. 服务常用管理指令
# 重载 systemd 配置文件
sudo systemctl daemon-reload
# 启动服务
sudo systemctl start my_script
# 设置开机自启动
sudo systemctl enable my_script
# 查看运行状态及最近日志
sudo systemctl status my_script
# 实时查看由 systemd 收集的日志流
sudo journalctl -u my_script -f
六、交互式长任务方案:tmux 终端复用器
如果运行的 Python 脚本在执行过程中可能需要人工输入参数确认,或者需要随时恢复图形化控制台进度条(如 tqdm),nohup 无法提供交互会话的重新接入。此时使用 tmux 是最理想的选择。
tmux 允许用户在服务器端建立一个与本地 SSH 彻底解耦的虚拟会话。即使用户断开网络,会话依然在云端后台活跃,并且可以结合 tee 命令一边在屏幕观察一边写盘:
# 1. 创建并进入名为 task 的专属 tmux 会话
tmux new -s task
# 2. 在 tmux 窗口中启动脚本,使用 tee 同时输出到屏幕与文件
python3 -u script.py 2>&1 | tee -a app.log
# 3. 按键盘快捷键 Ctrl + B,随后松开并单按 D 键,安全分离(Detach)会话返回母机终端
# 此时已可以安全关闭 SSH 连接
# 4. 重新连接 SSH 后,随时恢复进入该会话:
tmux attach -t task
七、后台运行四大方案选型对比
根据任务的生命周期、容错要求以及管理权限,下表整理了主流方案的技术特性与选型依据:
| 方案名称 | 适用场景 | 开机自启 | 崩溃恢复 | 操作复杂度 |
|---|---|---|---|---|
| nohup + & | 单次执行、快速测试、临时爬虫、排查脚本 | 否 | 无(退出即终止) | 极低(单行命令) |
| tmux / screen | 交互式脚本、需频繁重连看控制台进度条、多窗调试 | 否 | 无 | 低(需熟悉快捷键) |
| systemd 服务 | 生产业务、核心守护进程、API 服务、MQ 消费者 | 是 | 高(毫秒级自拉起) | 中等(需 root 配置) |
| Supervisor | 多 Python 脚本批量编排、普通用户权限下的进程守护 | 依赖系统服务 | 高(应用级守护) | 中等(需配置 ini) |
八、生产环境运行的五项避坑法则
为了保证后台任务长期稳定运行,不因外部路径变动或磁盘耗尽产生隐患,请务必在部署时遵守以下规范:
- 一律使用绝对路径:后台任务脱离了用户的当前终端上下文,如果脚本内依赖相对路径读取配置(如
./config.json),在某些场景下其工作路径可能漂移到系统根目录导致FileNotFoundError。建议脚本代码内部通过os.path.dirname(os.path.abspath(__file__))锁定基准路径; - 使用虚拟环境的独立解释器:避免在命令中直接使用全局的
python3。建议指定明确的虚拟环境解释器完整路径,例如/data/venv/bin/python3,防止系统更新全局包时引发依赖破坏; - 警惕日志无限制膨胀:长时间运行的高吞吐量脚本很容易产生几十 GB 的巨大日志,进而占满服务器磁盘 inodes 或存储空间。应结合系统
logrotate工具对app.log进行定时归档、压缩与清理; - 确认进程独占性:在通过
nohup重新拉起程序前,务必先用ps aux | grep script.py检查旧版本是否仍在运行,避免出现两个进程同时读写相同资源或并发冲突; - 优先使用 SIGTERM 退出:在编写 Python 代码时,建议注册
signal.signal(signal.SIGTERM, handler)信号监听回调,以便在被运维管理工具停止时,能够安全提交事务并关闭数据库连接池。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













