subprocess 是 Python 内置的子进程管理标准库 ,核心作用是在 Python 代码中调用外部系统命令、启动其他程序 / 脚本,并能控制它们的输入输出、获取执行结果、监控运行状态。 简单说:你可以在 Python 程序里,直接执行 cmd / 终端命令、运行其他 exe 程序、调用别的脚本,就像你手动在命令行里操作一样,全程由代码自动完成。
它替代了老旧的 os.system、os.popen 等工具,功能更强大、控制更精细,是写自动化脚本、批量处理、工具调用的必备模块。
本文从基础概念、核心函数到实战场景、避坑指南,全程配可运行代码 + 逐行解释,零基础也能完全吃透。
一、前置基础概念
1. 通俗理解
你可以把当前运行的 Python 程序理解成「主办公室」:
subprocess就是帮你出去跑腿的员工,可以帮你启动别的程序(比如打开记事本、执行 cmd 命令、运行另一个 Python 脚本)- 这些被启动的程序叫「子进程」,和主程序独立运行,你可以让它跑完等结果,也可以让它在后台自己跑
- 你能拿到它的输出、判断它有没有执行成功、甚至给它传输入内容
2. 能做什么?
- 执行系统命令:比如 Windows 的
dir、ping,Mac/Linux 的ls、ps - 调用第三方工具:比如调用
git、ffmpeg、压缩解压工具 - 运行其他脚本 / 程序:比如在 Python 里调用另一个 Python 脚本、exe 程序
- 自动化批量操作:比如批量执行命令、批量处理文件
- 获取命令输出:把命令执行的结果拿回来,在 Python 里继续处理
3. 模块说明
- 内置标准库:不需要安装,直接
import subprocess即可使用 - 推荐核心函数:Python 3.5+ 统一推荐用
subprocess.run(),覆盖 90% 日常场景 - 进阶复杂场景用
subprocess.Popen,实现后台运行、实时交互等高级功能
二、入门核心:subprocess.run ()
subprocess.run() 是最基础、最常用的函数,作用是执行指定命令,等待命令执行完毕后,返回执行结果。
1. 第一个最简单的例子
先执行一条最简单的 echo 命令,直观感受用法:
python
import subprocess
# 执行命令:echo 你好,Python
# Windows 系统需要加 shell=True,因为 echo 是 cmd 内置命令
result = subprocess.run(["echo", "你好,Python"], shell=True, text=True)
print("命令执行完毕")
逐行解释
subprocess.run():启动子进程执行命令,会阻塞等待命令执行完,才继续往下走- 第一个参数
["echo", "你好,Python"]:要执行的命令,用列表形式拆分「命令 + 参数」,是最推荐的安全写法 shell=True:表示用系统的命令解释器(cmd /bash)来执行命令。系统内置命令(echo、dir、cd 等)必须加这个参数,否则会找不到命令text=True:让输入输出都用字符串格式,不然默认返回的是 bytes 字节,中文会乱码
💡 系统差异说明:
- Windows:内置命令(dir、echo、cd)需要
shell=True;独立程序(python、git)可以不用- Mac/Linux:ls、pwd 等大多是独立程序,一般不用
shell=True
2. 返回值:CompletedProcess 对象
run() 执行完会返回一个 CompletedProcess 对象,里面包含了命令执行的所有结果,最常用三个属性:
| 属性 | 含义 | 说明 |
|---|---|---|
.returncode |
退出状态码 | 0 表示执行成功,非 0 表示执行失败 / 出错 |
.stdout |
标准输出内容 | 命令正常打印的结果 |
.stderr |
标准错误输出 | 命令报错时的错误信息 |
示例:获取命令执行结果
python
import subprocess
# 执行 python --version 命令,捕获输出
# capture_output=True:把命令的输出捕获下来,存在stdout/stderr里
result = subprocess.run(
["python", "--version"],
capture_output=True,
text=True
)
print("是否成功:", result.returncode == 0)
print("正常输出:", result.stdout)
print("错误输出:", result.stderr)
运行结果示例:
python
是否成功: True
正常输出: Python 3.11.4
错误输出:
关键参数解释
capture_output=True:捕获命令的标准输出和错误输出,不然输出会直接打印到控制台,拿不到内容- 如果你只需要标准输出,也可以单独写
stdout=subprocess.PIPE,和capture_output=True效果类似
三、最常用参数详解
subprocess.run() 有很多参数,新手掌握下面 7 个就能应对绝大多数场景:
| 参数 | 作用 | 新手使用建议 |
|---|---|---|
args |
要执行的命令,推荐用列表形式 ["命令", "参数1", "参数2"] |
优先用列表,安全可控 |
shell |
是否用系统 shell 执行命令 | 内置命令加 True,外部程序尽量不加 |
capture_output |
是否捕获输出(stdout+stderr) | 需要拿结果就加 True |
text |
是否用字符串模式(默认是 bytes) | 建议一律加 True,避免乱码 |
encoding |
指定编码,比如 gbk、utf-8 |
Windows 中文乱码时指定 gbk |
check |
命令失败(returncode≠0)时自动抛异常 | 需要严格校验成功与否时加 True |
timeout |
超时时间,单位秒,超时自动终止命令 | 防止命令卡死 |
下面逐个演示常用场景。
四、常用场景实战(带完整例子 + 解释)
场景 1:执行命令并捕获输出
这是最常用的场景:执行命令,把输出拿回来在 Python 里处理。
示例:执行 ping 命令检测网络
python
import subprocess
# Windows 下 ping 4 次百度
# -n 4 表示发送4个包,Windows是-n,Mac/Linux是-c
result = subprocess.run(
["ping", "-n", "4", "www.baidu.com"],
capture_output=True,
text=True,
encoding="gbk" # Windows cmd 默认是gbk编码,指定后中文不会乱码
)
print("执行结果:")
print(result.stdout)
if result.returncode == 0:
print("✅ 网络通畅")
else:
print("❌ 网络不通")
解释
encoding="gbk":Windows 命令行默认用 GBK 编码,不加的话中文会变成乱码,这是新手高频踩坑点- Mac/Linux 一般默认是 UTF-8,不用额外指定
场景 2:命令失败自动报错(check 参数)
默认情况下,哪怕命令执行失败,Python 也不会报错,只会把错误信息存在 stderr 里。 加 check=True 后,只要命令返回非 0 状态码,就会直接抛出 CalledProcessError 异常,适合严格校验的场景。
python
import subprocess
try:
# 执行一个不存在的命令,加check=True会自动报错
subprocess.run(
["xxx_not_exist_cmd"],
check=True,
capture_output=True,
text=True
)
except subprocess.CalledProcessError as e:
print("命令执行失败!")
print("错误信息:", e.stderr)
场景 3:给命令传递输入内容(stdin)
有些命令需要用户输入内容(比如交互式程序),可以通过 input 参数直接把内容传进去。
示例:给 Python 解释器传代码执行
python
import subprocess
# 要输入的代码
code = """
print("hello from subprocess")
print(1 + 2 + 3)
"""
result = subprocess.run(
["python"],
input=code, # 把code作为标准输入传给子进程
capture_output=True,
text=True
)
print("子进程输出:")
print(result.stdout)
运行结果:
python
子进程输出:
hello from subprocess
6
场景 4:设置超时,防止卡死
有些命令可能会卡住(比如死循环、网络超时),加 timeout 参数可以强制终止,避免主程序一直等。
python
import subprocess
try:
# 执行一个会睡10秒的命令,设置超时2秒
subprocess.run(
["python", "-c", "import time; time.sleep(10)"],
timeout=2
)
except subprocess.TimeoutExpired:
print("⏰ 命令超时,已强制终止")
超时后会抛出 TimeoutExpired 异常,子进程会被强制终止。
场景 5:调用另一个 Python 脚本
这是自动化里非常实用的场景:在主程序里调用另一个脚本,还能拿到它的输出。
第一步:先准备一个子脚本 sub_script.py
python
# sub_script.py
print("我是子脚本,开始运行")
print("计算结果:", 100 + 200)
第二步:主脚本调用它
python
import subprocess
result = subprocess.run(
["python", "sub_script.py"],
capture_output=True,
text=True
)
print("子脚本输出:")
print(result.stdout)
拓展:还可以通过命令行参数给子脚本传参,配合
sys.argv接收,实现更灵活的调用。
五、进阶:subprocess.Popen(后台运行 / 实时交互)
subprocess.run() 是「阻塞等待」的:必须等子进程跑完,才会继续往下走。 如果需要启动后台进程、实时读取输出、和进程交互 ,就要用更底层的 subprocess.Popen。
1. 什么时候用 Popen?
- 启动程序后不用等它结束,主程序继续往下跑(比如打开记事本、启动服务)
- 需要实时逐行读取命令输出
- 需要和子进程反复交互(输入、输出、再输入)
2. 示例 1:后台启动程序,不阻塞主程序
python
import subprocess
import time
# 启动Windows记事本,不等待,主程序继续运行
# Popen 不会阻塞,启动完立刻继续往下执行
proc = subprocess.Popen(["notepad.exe"])
print("记事本已经启动了,主程序继续运行...")
# 主程序可以继续做别的事
for i in range(5):
print(f"主程序运行中... {i+1}")
time.sleep(1)
# 可以主动终止子进程
# proc.terminate()
运行后会发现:记事本打开的同时,Python 主程序还在继续打印,不会卡住。
3. 示例 2:实时逐行读取命令输出
run() 是等命令全部跑完才一次性返回所有输出;Popen 可以边运行边读输出,适合长时间运行、有进度输出的命令。
python
import subprocess
import sys
# 执行一个逐行输出的命令
proc = subprocess.Popen(
["python", "-c", "import time; [print(f'第{i}行', flush=True) or time.sleep(0.5) for i in range(5)]"],
stdout=subprocess.PIPE,
text=True
)
# 逐行读取输出,实时打印
print("实时输出:")
for line in proc.stdout:
print("👉", line.strip())
# 等待进程结束,获取退出码
proc.wait()
print("命令结束,退出码:", proc.returncode)
注意:子进程输出如果有缓冲,可能不会实时打印,一般加
flush=True强制刷新缓冲区。
六、新手高频踩坑 & 避坑指南
1. 命令到底用列表还是字符串?要不要加 shell=True?
这是新手最容易混乱的点,直接给结论: ✅ 优先用列表形式,不加 shell=True
- 优点:安全,不会有命令注入风险,参数自动转义
- 适用:调用独立程序(python、git、ffmpeg 等)
⚠️ 只有系统内置命令才加 shell=True
- 比如 Windows 的
dir、echo、cd,Mac/Linux 的cd等 - 风险:如果命令里拼接了用户输入的内容,可能被注入恶意命令,有安全风险
❌ 反面教材(有注入风险):
python
# 危险!如果用户输入 "; del /f /s /q C:\*",就会执行恶意命令
user_input = input("请输入文件名:")
subprocess.run(f"dir {user_input}", shell=True)
✅ 安全写法:
python
# 用列表传参,自动转义,不会被注入
user_input = input("请输入文件名:")
subprocess.run(["dir", user_input], shell=True)
2. 输出是乱码 /bytes 看不懂
- 原因:默认 stdout/stderr 是 bytes 类型
- 解决:加
text=True参数,自动转成字符串 - Windows 中文还是乱码:加
encoding="gbk",因为 cmd 默认是 GBK 编码
3. 提示「不是内部或外部命令」
两种原因:
- 命令是系统内置命令,没加
shell=True→ 加上即可 - 程序没有安装,或者不在系统环境变量里 → 写程序的完整路径,或者配置环境变量
4. 以为 cd 命令能切换主程序目录
subprocess 里执行 cd 命令,只会改变子进程的工作目录,不会影响当前 Python 主程序的目录 。 如果要指定子进程的工作目录,用 cwd 参数:
python
# 在指定文件夹里执行命令
subprocess.run(["dir"], shell=True, cwd="C:/Users/xxx/Desktop")
5. 命令一直卡住不结束
- 大概率是子进程在等输入,或者陷入死循环
- 解决:加
timeout参数设置超时,或者检查命令是否需要交互输入
七、安全重要提醒
- 永远不要把不可信的用户输入拼进 shell 命令里 ,尤其是加了
shell=True的时候,会导致命令注入漏洞。 - 优先用列表形式传参,让 subprocess 自动处理转义,安全系数高很多。
- 执行外部命令前,尽量校验参数合法性。