1. subprocess 是什么? #

能力 说明
创建子进程 在 Python 中运行外部命令
捕获输出 把 stdout/stderr 保存到变量
检查返回码 0 表示成功,非 0 表示失败
超时控制 超时自动终止子进程

2. subprocess.run() 基本用法 #

# 导入 subprocess 模块
import subprocess

# 输出直接显示在终端
# 通过 cmd /c 执行 dir 命令(Windows 内置命令)
subprocess.run(["cmd", "/c", "dir"])

# 跨平台执行 Python 代码
# 导入 sys 以获取当前 Python 解释器路径
import sys
# 用当前解释器执行一行 print 代码
subprocess.run([sys.executable, "-c", "print('hello')"])

4. run() 常用参数 #

参数 作用
args 命令列表,如 ["ping", "-n", "2", "host"]
capture_output=True 捕获 stdout 和 stderr
text=True 输出为字符串(否则为 bytes)
check=True 返回码非 0 时抛异常
timeout=N 超时(秒)后终止
shell=True 通过 shell 执行(不推荐)

4.1 capture_output #

# 导入 subprocess 模块
import subprocess

# 执行 ping 命令并等待完成
result = subprocess.run(
    # 命令列表:ping 本机 2 次
    ["ping", "-n", "2", "127.0.0.1"],
    # 捕获 stdout 和 stderr
    capture_output=True,
    # 输出以字符串形式返回
    text=True,
    # 超时 5 秒后终止子进程
    timeout=5
)

# 打印标准输出内容
print(result.stdout)
# 打印返回码(0 表示成功)
print("返回码:", result.returncode)

4.2 check #

# 导入 subprocess 模块
import subprocess

# 错误处理示例
# 尝试执行命令并捕获可能的异常
try:
    # 执行 dir 并捕获输出,失败时抛异常
    result = subprocess.run(["cmd", "/c", "dir"], capture_output=True, text=True, check=True)
    # 打印标准输出内容
    print(result.stdout)
# 捕获命令返回非 0 的异常
except subprocess.CalledProcessError as e:
    # 打印失败时的返回码
    print(f"命令失败,返回码: {e.returncode}")
# 捕获超时异常
except subprocess.TimeoutExpired:
    # 提示命令执行超时
    print("命令超时")

4.3 不关心输出时 #

# 导入 subprocess 模块
import subprocess
# 执行命令但丢弃所有输出
subprocess.run(
    # 通过 cmd /c 执行 dir
    ["cmd", "/c", "dir"],
    # 标准输出重定向到空设备
    stdout=subprocess.DEVNULL,
    # 错误输出重定向到空设备
    stderr=subprocess.DEVNULL,
)

5. Popen 与实时输出 #

# 导入 subprocess 模块
import subprocess
# 导入 sys 获取解释器路径
import sys

# 创建子进程,不阻塞等待结束
proc = subprocess.Popen(
    # 用当前 Python 执行循环打印代码
    [sys.executable, "-c", "for i in range(3): print(i)"],
    # 创建 stdout 管道供主进程读取
    stdout=subprocess.PIPE,
    # 将 stderr 合并到 stdout
    stderr=subprocess.STDOUT,
    # 输出以字符串形式返回
    text=True,
)

# 逐行读取子进程的实时输出
for line in proc.stdout:
    # 打印每一行(保留原有换行行为)
    print("实时:", line, end="")

# 等待子进程执行完毕
proc.wait()
# 打印子进程最终返回码
print("返回码:", proc.returncode)

6. 安全:避免 shell=True #

# 导入 subprocess 模块
import subprocess
# 导入 sys 获取解释器路径
import sys

# 危险示例:用户输入可能注入额外命令
user_input = "hello & dir"
subprocess.run(f"echo {user_input}", shell=True)
# 导入 subprocess 模块
import subprocess
# 导入 sys 获取解释器路径
import sys
# 安全做法:用列表传参,避免 shell 解析
# 模拟包含特殊字符的用户输入
user_input = "hello & dir"
# 通过列表传参安全执行,输入作为字符串字面量传递
subprocess.run([sys.executable, "-c", f"print({user_input!r})"])

!r 是 Python 中 f-string(格式化字符串)的一个转换标志。

它的作用是:在将变量插入字符串之前,先对该变量调用 repr() 函数,得到它的“可打印表示”(通常带引号,并对特殊字符进行转义)。

7. 常见异常 #

异常 触发条件
CalledProcessError check=True 且返回码 ≠ 0
TimeoutExpired 超过 timeout 秒
FileNotFoundError 找不到可执行程序

8. 总结 #

8.1 速查 #

# 导入 subprocess 和 sys 模块
import subprocess, sys

# 执行并捕获输出
# 运行 dir 命令并获取结果对象
result = subprocess.run(
    # Windows 下通过 cmd /c 执行内置命令
    ["cmd", "/c", "dir"],
    # 捕获输出、文本模式、失败抛异常、超时 30 秒
    capture_output=True, text=True, check=True, timeout=30,
)
# 打印标准输出内容
print(result.stdout)

# 不关心输出时丢弃 stdout 和 stderr
subprocess.run(["cmd", "/c", "dir"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

# 用当前 Python 解释器运行外部脚本
subprocess.run([sys.executable, "script.py", "arg1"])

8.2 最佳实践 #