春江暮客

春江暮客的个人学习分享网站

Linux 定时任务失败排查:固定 Python 路径、工作目录和环境变量

2026-10-11 技术
Linux 定时任务失败排查:固定 Python 路径、工作目录和环境变量

终端里运行 Python 一切正常,到了定时任务却报 FileNotFoundError、python3: not found 或缺少配置。常见原因是任务依赖了当前目录、终端的 PATH,或之前激活的虚拟环境。

本文面向初学者到中级读者,用一个三行数据的任务分别复现这些问题,再写一个固定入口。实验不需要启动 cron,也不需要修改 crontab;先把命令在精简环境里跑通,再接入已有调度器。

1. 方法:把运行条件写进入口

Cronie 官方 crontab 手册说明,任务按 crontab 所属用户运行,默认 SHELL 是 /bin/sh,HOME 和 LOGNAME 根据该用户设置。不要据此假定任务会读取交互终端的启动配置;其他调度器也可能采用不同环境。

本文显式确定三件事:项目工作目录、Python 解释器路径、必要变量。只输出用于排查的少量设置,避免把整套环境里的凭据打印进日志。

2. 准备环境

需要 Linux、Python 3.9 或更新版本及其 venv 模块、GNU env 和 /bin/sh。示例已在 Linux、Python 3.11.4 下验证。Hugo 只用于第 7 节,无第三方 Python 依赖。

  1. 在 bash 或 zsh 中新建演示目录,重复实验时使用新目录名。下面后续命令都在同一个终端执行,保留 job_root 变量:
python3 --version
command -v python3
command -v env
env --help
mkdir cron-env-demo
cd cron-env-demo
job_root="$PWD"
python3 -m venv --without-pip .venv
printf 'alpha\nbeta\ngamma\n' > data.txt
mkdir empty-bin

--without-pip 只创建本地虚拟环境,不安装任何包。如果系统缺少 venv,请使用已有的项目环境,或请管理员提供对应模块,不要改系统 Python 的默认链接。

  1. 将下一节代码保存为 job.py,也可以下载 job.py。

3. 写一个可诊断的 Python 任务

"""Inspect the small set of settings this demo needs. Python 3.9+."""
import os
from pathlib import Path
import sys


def main():
    print(f"cwd={Path.cwd()}", flush=True)
    print(f"python={sys.executable}", flush=True)
    print(f"venv={sys.prefix != sys.base_prefix}", flush=True)
    print(f"PATH={os.environ.get('PATH', '<unset>')}", flush=True)
    try:
        rows = Path("data.txt").read_text(encoding="utf-8").splitlines()
    except OSError as exc:
        print(f"ERROR: {exc}", file=sys.stderr)
        return 1
    if os.environ.get("REPORT_MODE") != "daily":
        print("ERROR: REPORT_MODE must be daily", file=sys.stderr)
        return 2
    count = sum(bool(row.strip()) for row in rows)
    print(f"OK: records={count}; mode=daily", flush=True)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

先从项目目录运行:

REPORT_MODE=daily "$job_root/.venv/bin/python" "$job_root/job.py"

输出中的 venv=True 确认实际用了虚拟环境,最后一行应为 OK: records=3; mode=daily。

Python venv 文档说明,指定虚拟环境解释器的完整路径即可使用它,不必先激活;sys.prefix != sys.base_prefix 可用于判断。不要仅依赖 VIRTUAL_ENV 变量。

4. 分别复现三个错误

错误一:脚本路径正确,输入文件仍找不到

(
  cd /
  env -i PATH=/usr/bin:/bin REPORT_MODE=daily \
    "$job_root/.venv/bin/python" "$job_root/job.py"
)
printf 'exit=%s\n' "$?"

输出显示 cwd=/、找不到 data.txt,最后是 exit=1。脚本的绝对路径不会自动改变工作目录,相对输入路径仍从当前目录查找。

错误二:PATH 中没有解释器

env -i PATH="$job_root/empty-bin" python3 --version
printf 'exit=%s\n' "$?"

这里故意只搜索一个空目录,预期找不到 python3,返回 exit=127。它独立演示搜索路径问题,不代表每台服务器的 cron 都会找不到 Python。

错误三:配置只在交互终端里设置过

当前仍在项目目录,输入文件存在,但这次不给 REPORT_MODE:

env -i PATH=/usr/bin:/bin \
  "$job_root/.venv/bin/python" "$job_root/job.py"
printf 'exit=%s\n' "$?"

预期是 ERROR: REPORT_MODE must be daily 和 exit=2。

GNU env 官方说明中,-i 表示从空环境开始,再加入命令行赋值。这个实验比很多实际调度环境更精简,用于发现隐藏依赖;它不模拟调度器的用户、权限、时区或服务限制。

5. 修复:固定目录、解释器和变量

将以下内容保存为 run_job.sh,放在 job.py 旁边,也可以下载 run_job.sh。使用实际路径调用它,不通过指向其他位置的符号链接运行。

#!/bin/sh
set -eu
PATH=/usr/bin:/bin
export PATH
job_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
cd "$job_dir"
REPORT_MODE=daily
export REPORT_MODE
exec "$job_dir/.venv/bin/python" -u "$job_dir/job.py"

入口先确定可信的工具搜索路径,再从自己的位置取得项目目录,切换目录,设置任务需要的非敏感变量,最后使用本地解释器执行。exec 让 Python 的退出码直接成为入口退出码;-u 让输出及时进入重定向日志。

这里的 REPORT_MODE 不是密钥。实际任务需要凭据时,沿用部署中已有的安全注入方式;不要照着示例把密钥写入文章或脚本。

6. 验证修复后的入口

从根目录、空环境启动入口,并把两种输出流写入演示日志:

(
  cd /
  env -i PATH=/usr/bin:/bin /bin/sh "$job_root/run_job.sh"
) > "$job_root/job.log" 2>&1
job_status=$?
cat "$job_root/job.log"
printf 'exit=%s\n' "$job_status"

cwd 应指向演示目录,python 应指向该目录的 .venv/bin/python,末尾应为:

OK: records=3; mode=daily
exit=0

重复执行应仍统计三条记录。示例用 > 覆盖演示日志;真实任务需要保留历史时,可以参考 Python 日志轮转教程。诊断入口稳定后,再把它作为已有调度器的命令入口,并用实际运行账户验证权限。

若 Python 内部还要调用外部程序,可以配合 子进程超时与日志教程,分别检查子命令退出码和业务输出。

7. 同样的方法用于 Hugo 临时构建

Hugo 也可能只安装在终端额外加入的目录里。以下命令需要当前终端能找到 hugo:先记录完整路径,创建一个本地最小站点,再从 / 构建到全新的临时目录。

command -v hugo
hugo version
hugo --help
hugo_bin="$(command -v hugo)"
mkdir -p mini-site/content mini-site/layouts
printf 'baseURL = "https://example.org/"\n' > mini-site/hugo.toml
printf '# Scheduled preview\n' > mini-site/content/_index.md
cat > mini-site/layouts/index.html <<'HTML'
<!doctype html><html lang="en"><head><title>Demo</title></head><body>{{ .Content }}</body></html>
HTML
preview_root="$(mktemp -d)"
(
  cd /
  env -i PATH=/usr/bin:/bin "$hugo_bin" \
    --source "$job_root/mini-site" \
    --cacheDir "$preview_root/cache" \
    --destination "$preview_root/site"
)
test -s "$preview_root/site/index.html"
cat "$preview_root/site/index.html"

最后输出的 HTML 应包含 Scheduled preview。示例已用 Hugo 0.164.0 验证;它不发布站点,输出目录每次由 mktemp 新建。

Hugo 官方命令说明列出了 --source、--cacheDir 和 --destination。实际项目先用它们做预览,再接入自己的发布流程。若用 GitHub Actions 执行构建,可以参考 Hugo 自动构建与发布教程。

8. 常见错误与直接处理

现象 处理方法
FileNotFoundError 或示例中的文件读取错误 对照日志的 cwd,进入项目目录,或给输入文件传入绝对路径。
python3: not found 记录项目解释器路径,并直接调用 .venv/bin/python。
ModuleNotFoundError 先确认日志中的解释器,再用同一个解释器检查项目依赖;不要先全局安装。
REPORT_MODE must be daily 在入口显式提供必要变量。
日志文件没有生成 检查重定向目标的父目录及运行账户的写权限;shell 可能在启动任务前就失败。
Hugo 找不到配置、主题或命令 确认 Hugo 可执行文件与站点根目录;对照版本,使用明确的 --source。

示例验证了三种失败、修复后的成功与重复运行,也检查了入口在输入文件缺失时保留非零退出码。精简环境测试通过后,仍要检查真实调度运行的日志;本文没有测试或修改实际 cron 配置。

9. 小结

先把工作目录、解释器和必要变量记录清楚,再用精简环境复现错误。将已验证的条件写入固定入口,任务就能减少对交互终端状态的依赖。发布和批处理流程都应保留退出码,并继续检查结果文件。

标签

1024 12306 ablang adsense agents.md ai ai-agent ai-agents ai-seo algorithm amp antibodies apparmor automation batch-processing binarycif bioinformatics biopython blockchain boltz bootstrapping boxes bubblewrap c-index cca cdn chatgpt checkpoint cli cloudflare codex cofoldarena copy cpu监控 cron csv cuda curl data-leakage data-processing data-quality data-validation datascience datavisualization deployment desktop-app devtools disk-space disown docker dovecot download electron esm esm2 esm3 esmc esmfold2 faceswap fasta fastmcp ffmpeg file-io flashppi flask folium frontend game generator git github-actions google google-research grep harness hls html http hugo indexnow javascript jev json just k-means kaggle langfuse leecode linux list litellm llm llms.txt logging logs lollipop lsof m3u8 machine-learning macos manacher matplotlib mcp mirror mmcif model-evaluation mp3 mp4 mpnn multiomics mutation mysql nanobert nanobodies networkx nginx normalize numpy ollama omegatherm pandas password pdb pep-723 phaser pillow pip postfix preprocessing print protein-alignment protein-design protein-embeddings protein-interactions protein-language-models protein-stability protein-structure proxy pydantic pyecharts pyqt python python3 r raincloud reproducibility requests reservoir-sampling rfoptimization rg ripgrep rosettafold3 roundcube rrsi rsi rsync s-tui sampling scale scikit-learn scrapy screen seaborn security selenium seo sequence-identity sha256 sklearn solana somaticsignatures spl sqlite ssh standardize static-site subprocess sysadmin system-one tensorflow tkinter tron tronpy troubleshooting turtle typesafe-ai ubuntu usdt uv vhhbert vite webp wordcloud wordpress workflow yaml 后台 寓言 概率 经济 贸易 迅雷解析 钱包

友情链接

其它