Python subprocess 子进程完全指南

subprocess 让 Python 调用外部程序:run 执行命令、捕获输出、错误处理、传输入、shell 与列表参数的安全差异、Popen 高级控制。本文讲透每个细节。

最佳实践
并发任务的多路径协同插画

直接回答:subprocess 是 Python 启动与控制外部进程的标准模块:run() 一行执行命令并拿结果,capture_output 捕获输出,check 自动抛错,Popen 提供流式交互的高级控制;接口跨平台可用,但外部命令、信号和 shell 行为仍因系统而异。

基本执行

import subprocess

result = subprocess.run(["ls", "-l", "/tmp"], capture_output=True, text=True)
print(result.stdout)      # 标准输出
print(result.returncode)  # 退出码 0 = 成功

三要点记牢:参数用列表(每个参数独立元素)、capture_output=True 拿输出、text=True 得到字符串而非字节。

错误处理

try:
    subprocess.run(["git", "push"], check=True, capture_output=True, text=True)
except subprocess.CalledProcessError as e:
    print("命令失败:", e.returncode)
    print("错误输出:", e.stderr)

check=True 让非零退出码直接抛异常——默认不抛,忘记检查 returncode 是脚本事故的常客。再加 timeout=30 防止命令卡死拖住整程序。

向子进程传输入

result = subprocess.run(
    ["grep", "error"],
    input=log_text,          # 通过 stdin 喂数据
    capture_output=True,
    text=True,
)

管道在 Python 层面完成,不需要 shell 拼接。

shell=True:能力越大风险越大

# 方便:通配符、管道、重定向都能用
subprocess.run("cat *.log | grep ERROR > errors.txt", shell=True)

# 危险:用户输入注进来就是命令注入
filename = input()
subprocess.run(f"cat {filename}", shell=True)   # 输入 "; echo injected" 会被 shell 解释为额外命令

规则:优先参数列表且保持 shell=False。 参数列表仍需防止选项注入,例如支持 -- 的工具可使用 ["cat", "--", filename];生产环境还需限制可执行文件和可读路径。

Popen:高级控制

需要实时读输出、后台跑、双向交互时上 Popen:

proc = subprocess.Popen(
    ["python3", "worker.py"],
    stdout=subprocess.PIPE,
    stderr=subprocess.STDOUT,  # 合并输出,避免未消费的 stderr 管道阻塞
    text=True,
)

# 流式读输出
for line in proc.stdout:
    print("子进程说:", line.strip())

proc.wait()
print("退出码:", proc.returncode)

若输出有界,可改用 communicate(timeout=...) 一次读取两路输出,不能与上面的逐行消费方式混用。run(timeout=...) 超时会终止并等待直接子进程;Popen.communicate(timeout=...) 超时则不会自动终止,应在捕获 TimeoutExpired 后调用 kill(),再 communicate() 回收。进程树的后代还需平台相关的进程组管理。长任务记得消费输出管道——缓冲满了子进程会阻塞,双方死等。

环境变量与工作目录

import os
env = {**os.environ, "APP_ENV": "production"}

subprocess.run(
    ["make", "deploy"],
    cwd="/srv/myapp",     # 子进程的工作目录
    env=env,               # 定制环境
)

常见问题(FAQ)

Q:subprocess 和 os.system 的区别?
A:os.system 交给 shell 执行且控制能力有限;在 Unix 上返回的是 wait 状态,不是直接的退出码。需要参数、输入输出和错误控制时优先 subprocess。

Q:程序 hang 住不动?
A:一种常见原因是 PIPE 缓冲塞满:用了 stdout=PIPE 却不读。用 communicate() 或 run(capture_output=True),或输出重定向到文件。

Q:想并行跑多个命令?
A:多个 Popen 同时启动再逐个 wait;或用早于 Python 3.12 就已提供的 asyncio.create_subprocess_exec 走异步路线,更优雅。

官方参考

本文基于官方文档整理,未进行运行时或性能测试。示例中的业务函数、数据模型和部署地址需结合项目补全;局部片段不等同于完整生产应用。

延伸阅读

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台