停掉 /dev/null 重定向并显式输出到日志是排查隐蔽故障最有效、不可跳过的一步;因静默丢弃 stdout/stderr 会导致 stdin 阻塞、异常忽略、子进程失败不感知等软死锁隐患,须改用带时间戳的日志追加,并检查进程状态与日志断点。

直接停掉 /dev/null 重定向,把 stdout 和 stderr 显式输出到日志文件——这是排查隐蔽故障最有效、最不可跳过的一步。
为什么丢进 /dev/null 会埋下软死锁隐患
把所有输出都重定向到 /dev/null 并非“干净”,而是“掩耳盗铃”。脚本中一旦出现以下情况,错误会被彻底吞掉:
- Python 脚本调用
input()或getpass.getpass(),进程卡在 stdin 等待输入,状态显示为S(sleep),但无任何日志提示 - 数据库连接超时、API 返回 503、文件权限拒绝等异常被静默忽略,后续逻辑基于空/默认值继续执行,逐步污染数据或触发下游异常
- 子进程(如
os.system("java ..."))因环境变量缺失而退出码非 0,但主脚本未检查返回值,也无错误输出
立即生效的排查动作
不改代码,只改 crontab 行,快速暴露问题:
- 把原类似
*/10 * * * * /usr/bin/python3 /opt/job.py &>/dev/null的行,临时改为:*/10 * * * * /usr/bin/python3 /opt/job.py >> /var/log/job_debug.log 2>&1 - 加时间戳便于定位:
*/10 * * * * echo "=== $(date) ===" >> /var/log/job_debug.log 2>&1 && /usr/bin/python3 /opt/job.py >> /var/log/job_debug.log 2>&1 - 观察日志是否突然出现大量重复的
Traceback、Permission denied、Connection refused或长时间空白(暗示阻塞)
识别软死锁的关键线索
不是所有报错都会立刻崩溃。这些迹象更危险,需重点盯住:
- 进程状态长期为
S(sleep),但lsof -p $PID显示 fd 0(stdin)指向pipe或socket,而非/dev/null - 日志末尾停留在某一行后不再更新,且该行是
input(、getpass(或第三方库的交互式提示 - 同一任务在日志中出现“启动成功”但无“完成”标记,且间隔周期内反复启动新进程(用
ps aux | grep job.py查看是否存在多个同名进程)
修复后必须做的两件事
日志暴露问题只是开始,稳定运行需闭环:
- 对已确认的 stdin 阻塞点,在脚本开头强制接管:
import sys; sys.stdin = open('/dev/null'),放在任何可能读取输入的代码之前 - 输出重定向策略升级:日常运行仍可写入日志,但要加轮转和保留天数,例如用
logrotate管理/var/log/job.log,避免磁盘打满;绝不全量丢进/dev/null

















