定时任务脚本后台运行时因 stdin 不可用导致 input 类调用永久阻塞,表现为假死;应通过 crontab 或 systemd 重定向 stdin 到 /dev/null,并在脚本中主动替换 sys.stdin 防御性处理。

定时任务脚本在后台运行时,若未显式关闭或重定向标准输入(stdin),而脚本内部又意外调用了 input()、raw_input()、sys.stdin.readline() 或依赖交互式输入的库(如某些密码提示、配置确认逻辑),就会因 stdin 不可用而永久阻塞——进程卡住、不报错、不退出,表现为“假死”。这不是 Python 独有,但 Python 脚本中较易因开发阶段本地调试习惯遗留此类问题。
确认是否真因 stdin 阻塞
先验证挂起原因是否为等待输入:
- 用
ps aux | grep your_script查看进程状态,若显示S(sleep)或<code>R(running)但长时间无日志输出,需进一步排查 - 用
lsof -p <pid></pid>检查该进程打开的文件描述符:若0u(即 stdin)指向pipe、socket或异常设备(非/dev/null或CHR终端),且脚本含输入调用,基本可判定 - 临时在脚本开头加
import sys; print(f"stdin: {sys.stdin}", file=sys.stderr)并重定向 stderr 到日志,观察实际绑定对象
根本解决:启动时强制重定向 stdin
不依赖脚本内修改,从执行层切断输入源,最稳妥:
- 在
crontab中使用:<br><code>0 2 * * * /usr/bin/python3 /path/to/script.py > /var/log/script.log 2>&1 - 在 systemd service 文件的
ExecStart=后追加:ExecStart=/usr/bin/python3 /path/to/script.py - 若通过 shell 封装脚本,第一行加
exec
脚本内防御性处理
即使外部已重定向,也建议在脚本入口处主动接管 stdin,避免第三方模块误触发:
- Python 中可立即替换 stdin 为不可读对象:
import sys; sys.stdin = open('/dev/null')(需在任何可能读 stdin 的代码前执行) - 或更安全地捕获异常:
try: input("Press Enter to continue...") except (EOFError, OSError): pass - 检查是否在非交互环境运行:
if not sys.stdin.isatty(): sys.stdin = open('/dev/null')
排查常见诱因点
以下位置容易隐含 stdin 读取,需逐行审查:
-
getpass.getpass()—— 生产环境必须改用环境变量或配置文件传密钥 -
argparse.ArgumentParser().parse_args()默认不读 stdin,但若自定义type或action函数里调用了input,会中招 - 日志配置中误设
input作为 handler 参数(极少见,但自定义 handler 时可能) - 调用的子命令(如
subprocess.run(["some-cli"], ...))本身是交互式程序,未加stdin=subprocess.DEVNULL

















