Python 调用命令行工具:把参数、失败和超时处理清楚
写自动化脚本时,经常要调用另一个程序:转换文件、生成报告,或者运行已有工具。命令在终端里能跑通,只是第一步。脚本还需要回答三个问题:参数有没有被正确传递,子进程是否成功,以及它会不会一直卡住。Python 标准库的 subprocess.run 可以把这些行为明确写出来。
参数用列表传递
如果把文件名拼进命令字符串,再交给 shell=True,空格、引号和 shell 特殊字符就可能改变原本的含义。对于“执行一个程序并传入参数”的场景,应优先使用参数列表,保持默认的 shell=False。
import subprocess
import sys
result = subprocess.run(
[
sys.executable, "-X", "utf8", "-c",
"import sys; print(sys.argv[1])",
"月报 2026 & 备份.txt",
],
capture_output=True,
text=True,
encoding="utf-8",
check=True,
timeout=5,
)
print(result.stdout.strip())
这里的文件名只是演示参数,并未创建文件。即使包含空格和 &,它仍作为一个参数交给 Python,不会被 shell 拆成其他命令。sys.executable 指向当前解释器,能减少虚拟环境里调用错 Python 的问题;示例中的 -X utf8 明确启用子进程的 UTF-8 模式。
参数列表也不是通用的输入校验。若目标工具把以 - 开头的文件名当作选项,还要根据工具文档使用 -- 等选项终止方式。程序路径、允许的操作和访问目录仍需由调用方控制。
失败要进入异常分支
run 默认不会因为退出码非零而抛出异常。若后续步骤依赖执行成功,显式设置 check=True,否则可能把失败时留下的不完整文件当成正常结果。
try:
subprocess.run(
[sys.executable, "-c", "raise SystemExit(7)"],
capture_output=True,
text=True,
encoding="utf-8",
check=True,
timeout=5,
)
except subprocess.CalledProcessError as exc:
print(f"工具执行失败,退出码:{exc.returncode}")
except subprocess.TimeoutExpired:
print("工具超时,停止后续处理")
except FileNotFoundError:
print("找不到要执行的程序,请检查安装和路径")
这三类失败对应不同处理:退出码非零应检查工具报错;超时应检查输入规模或进程状态;程序不存在则检查安装。不要遇到任何异常都直接无限重试,也不要把完整命令和输出无条件写进日志,其中可能含有令牌、文件内容或其他敏感信息。
超时和输出还有边界
timeout 以秒为单位。run 在等待超时后会终止并等待它直接启动的子进程,再抛出异常;这不等于跨平台清理整棵进程树。若工具还会派生后台服务,需要另行设计进程组或服务管理机制。部分平台的进程创建过程也不一定能被这一超时立即打断。
capture_output=True 会把标准输出和标准错误收集到内存,适合输出有限的小工具。对于持续运行或输出量很大的任务,更适合写入文件,或用 Popen 逐步读取,避免内存持续增长。也不要假设所有命令都输出 UTF-8:本例明确控制了子进程编码,调用其他工具时应遵循它实际使用的编码。
上线前可以准备四个小检查:正常输出中文、原样接收含空格的参数、主动返回非零退出码,以及休眠超过超时时间。先确认这些分支的行为,再把真正的转换工具接入流程,排查问题会轻松很多。
本文由 AI 辅助撰写;示例已在本地验证参数传递、UTF-8 输出、非零退出码和超时分支。
- 点赞
- 收藏
- 关注作者
评论(0)