subprocess 是 Python 标准库中的一个核心模块,用于创建子进程、连接它们的输入/输出/错误管道,并获取它们的返回码(退出状态),它让你可以在 Python 脚本中执行外部命令,就像是你在终端(命令行)里输入命令一样。

subprocess 是什么?为什么需要它?
在 subprocess 出现之前,Python 主要使用 os.system() 来执行外部命令,但 os.system() 有很多缺点:
- 不安全:容易受到命令注入攻击(如果命令参数来自用户输入)。
- 功能有限:无法方便地获取命令的输出或错误信息,只能获取退出码。
- 交互性差:无法与子进程进行实时的输入/输出交互。
subprocess 模块就是为了解决这些问题而设计的,它提供了更强大、更灵活、更安全的方式来管理子进程。
如何使用 subprocess (核心方法)
subprocess 模块提供了多种运行外部命令的方式,从简单到复杂,你可以根据需求选择。
前提条件
subprocess 是 Python 的标准库的一部分,这意味着它不需要安装!只要你安装了 Python,就已经拥有了这个模块。

你只需要在代码中导入它即可:
import subprocess
核心方法详解
subprocess.run() - 推荐使用 (Python 3.5+)
这是最现代、最推荐的方法,它非常灵活且功能强大。
基本语法:
subprocess.run(args, *, stdin=None, input=None, capture_output=False, shell=False, text=False, encoding=None, errors=None, timeout=None, check=False, ...)
参数解释:

args: 要执行的命令,可以是一个字符串(shell=True),或者一个字符串列表(推荐方式)。capture_output=True: 捕获标准输出和标准错误,如果设置为True,输出将存储在返回的CompletedProcess对象的stdout和stderr属性中。text=True或universal_newlines=True: 将捕获的输出作为文本字符串返回,而不是字节,这通常更方便。check=False: 如果设置为True,并且进程以非零状态码(即错误)退出,则会引发CalledProcessError异常。shell=False: 是否使用 shell 解释器。安全起见,通常设为False。timeout: 设置命令执行的超时时间(秒)。
示例 1: 执行一个简单命令并获取输出
import subprocess
# 执行 'ls -l' 命令
# 注意:在 Windows 上,命令应该是 'dir'
result = subprocess.run(['ls', '-l'], capture_output=True, text=True, check=True)
# 检查返回码
print(f"返回码: {result.returncode}")
# 打印捕获的输出
print("标准输出:")
print(result.stdout)
# 如果命令出错,标准错误会在这里
# print("标准错误:")
# print(result.stderr)
示例 2: 命令执行失败时处理错误
import subprocess
try:
# 故意执行一个会失败的命令
subprocess.run(['ls', 'non_existent_file'], capture_output=True, text=True, check=True)
except subprocess.CalledProcessError as e:
print(f"命令执行失败,返回码: {e.returncode}")
print(f"标准错误: {e.stderr}")
subprocess.Popen() - 最底层、最灵活
这是 subprocess 模块中最底层的类,当你需要更高级的控制,比如与子进程进行实时交互、同时读取输出和错误流时,就需要使用它。
基本语法:
subprocess.Popen(args, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, ...)
与 run() 的主要区别:
Popen是异步的,它启动子进程后会立即返回,不会等待子进程结束。- 你需要手动调用
process.communicate()来等待进程结束并获取输出。 - 你可以手动调用
process.wait()来等待进程结束。 - 你可以获取进程的
pid(进程ID)。
示例:
import subprocess
# 启动一个进程
process = subprocess.Popen(['ls', '-l'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
# 等待进程结束并获取输出
# communicate() 会读取所有输出并等待进程终止
stdout, stderr = process.communicate()
print(f"返回码: {process.returncode}")
print("标准输出:")
print(stdout)
if stderr:
print("标准错误:")
print(stderr)
旧版便捷函数 (不推荐在新代码中使用)
在 run() 出现之前,Python 提供了一些便捷函数,它们现在仍然可用,但功能有限,不推荐在新项目中使用。
subprocess.call(): 执行命令,等待其完成,然后返回返回码,不获取输出。subprocess.check_call(): 类似于call(),但如果返回码不为零,则引发异常。subprocess.check_output(): 执行命令,等待其完成,然后返回标准输出,如果返回码不为零,则引发异常。subprocess.getoutput(): (旧版) 执行命令并返回输出,不安全。
示例 (不推荐):
# 等同于 run(..., check=True) 但无法轻易获取输出 subprocess.check_call(['ls', '-l']) # 等同于 run(..., capture_output=True, text=True, check=True) 的部分功能 output = subprocess.check_output(['ls', '-l'], text=True) print(output)
subprocess.run() vs. subprocess.Popen() - 如何选择?
| 特性 | subprocess.run() |
subprocess.Popen() |
|---|---|---|
| 易用性 | 高,简单直观 | 低,需要更多手动操作 |
| 推荐度 | 强烈推荐,适用于绝大多数场景 | 仅在需要高级功能时使用 |
| 同步/异步 | 同步的,会等待命令执行完毕 | 异步的,启动后立即返回 |
| 高级控制 | 有限 | 强大,可交互、并发控制等 |
| 适用场景 | 执行简单命令、获取输出、检查错误 | 与子进程实时交互(如 ssh, top)、管道连接多个命令 |
简单总结:
先用
subprocess.run(),如果它不能满足你的需求(比如你需要一边读取输出一边处理,而不是等所有输出都出来),再考虑使用subprocess.Popen()。
重要注意事项
安全性:shell=True 的风险
永远不要轻易使用 shell=True,特别是当命令参数来自用户输入时。
-
shell=False(默认,推荐):# 安全的方式 # Python 会直接将列表中的元素作为程序的参数执行 subprocess.run(['ls', '-l', user_input_variable]) # 这相当于在命令行执行: ls -l "用户输入的内容"
-
shell=True(危险):# 危险的方式! # Python 会先启动一个 shell (如 /bin/sh),然后将整个字符串作为命令让 shell 解释 user_input = "malicious_file; rm -rf /" # 命令注入 subprocess.run(f'ls -l {user_input}', shell=True) # 这相当于在命令行执行: sh -c "ls -l malicious_file; rm -rf /" # 这会先列出文件,然后尝试删除根目录下的所有文件!
跨平台兼容性
-
Windows vs. Linux/macOS:
- 命令不同:
dir(Windows) vs.ls(Linux/macOS)。 - 路径分隔符不同:
\(Windows) vs. (Linux/macOS)。 - 推荐使用
os模块来处理路径,使其跨平台兼容。import os path = os.path.join('folder', 'file.txt') # 自动使用正确的路径分隔符
- 命令不同:
-
可执行文件: 在非 Windows 系统上,
executable参数可能需要指定完整路径。
编码问题
在处理非 ASCII 字符(如中文)时,可能会遇到编码问题。
- 使用
text=True(或universal_newlines=True) 可以让subprocess自动处理编码。 - 如果仍然有问题,可以明确指定
encoding和errors参数。# 对于某些系统,明确指定编码更可靠 result = subprocess.run(['echo', '你好'], capture_output=True, text=True, encoding='utf-8', errors='ignore') print(result.stdout)
subprocess无需安装,它是 Python 标准库的一部分。- 首选
subprocess.run(),它功能强大、安全且易于使用。 - 避免使用
shell=True,除非你完全理解其风险并有特殊需求,否则存在命令注入的安全漏洞。 - 对于需要与子进程实时交互或进行复杂并发控制的场景,再考虑使用
subprocess.Popen()。 - 注意跨平台差异和编码问题,编写健壮的代码。
