使用 subprocess 模块 (最推荐)
subprocess 模块是 Python 官方推荐的用于创建和管理子进程的工具,它功能强大,灵活,并且旨在替代旧的 os.system 和 os.spawn* 等方法。

核心函数
-
subprocess.run()(Python 3.5+ 推荐) 这是最现代、最通用的接口,它会执行命令,等待它完成,然后返回一个CompletedProcess对象。 -
subprocess.Popen()这是最底层、最强大的接口,它启动一个进程,但不等待它完成,而是立即返回,你需要手动管理进程的生命周期(等待它结束、读取输出等),当你需要与子进程进行复杂的交互(双向通信)时,这个接口是必需的。
示例 1:使用 subprocess.run() 执行一个独立的 .py 文件
假设我们有两个文件:
main_script.py (调用者)

import subprocess
import sys
print("主脚本开始运行...")
# 定义要执行的子脚本路径
script_to_run = "sub_script.py"
try:
# 使用 run() 执行子脚本
# check=True 会在子脚本返回非零退出码(即出错)时抛出 CalledProcessError 异常
# capture_output=True 会捕获子脚本的 stdout 和 stderr
result = subprocess.run(
["python", script_to_run],
check=True,
capture_output=True,
text=True, # 将输出解码为文本
encoding='utf-8' # 指定编码
)
# 子脚本执行成功后,获取其输出
print("\n--- 子脚本执行成功 ---")
print(f"返回码: {result.returncode}")
print(f"标准输出: \n{result.stdout}")
# print(f"标准错误: \n{result.stderr}") # 如果子脚本有错误输出,可以在这里查看
except subprocess.CalledProcessError as e:
# 如果子脚本执行失败(返回码非零)
print(f"\n--- 子脚本执行失败 ---")
print(f"返回码: {e.returncode}")
print(f"标准输出: \n{e.stdout}")
print(f"标准错误: \n{e.stderr}")
print("\n主脚本运行结束。")
sub_script.py (被调用者)
import time
import sys
print("子脚本开始运行...")
print("正在执行一些任务...")
time.sleep(2) # 模拟耗时操作
# 从命令行参数中获取信息(可选)
if len(sys.argv) > 1:
print(f"从主脚本收到的参数: {sys.argv[1]}")
# 故意制造一个错误来测试
# print("这是一个错误信息", file=sys.stderr)
# sys.exit(1) # 以非零状态码退出,表示失败
print("子脚本运行成功,准备退出。")
sys.exit(0) # 以零状态码退出,表示成功
如何运行:
- 将
main_script.py和sub_script.py放在同一个目录下。 - 在终端中运行:
python main_script.py
输出:
主脚本开始运行...
--- 子脚本执行成功 ---
返回码: 0
标准输出:
子脚本开始运行...
正在执行一些任务...
子脚本运行成功,准备退出。
主脚本运行结束。
示例 2:向子脚本传递参数
只需要在 run() 的列表中添加参数即可。

修改 main_script.py:
# ... (前面的代码不变)
try:
# 向子脚本传递一个参数 "hello world"
result = subprocess.run(
["python", script_to_run, "hello world"],
check=True,
capture_output=True,
text=True
)
# ... (后面的代码不变)
运行 sub_script.py 的输出会变为:
子脚本开始运行...
正在执行一些任务...
从主脚本收到的参数: hello world
子脚本运行成功,准备退出。
示例 3:使用 subprocess.Popen() 进行交互
当你需要长时间运行一个进程,或者需要与它进行实时通信时,Popen 是最佳选择。
interactive_main.py
import subprocess
import sys
print("主脚本:使用 Popen 启动一个交互式子进程。")
# 启动子进程,不等待
# stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE 用于管道通信
proc = subprocess.Popen(
["python", "interactive_sub_script.py"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
encoding='utf-8'
)
# 向子进程发送输入
print("主脚本:向子进程发送 'greet'")
proc.stdin.write("greet\n")
proc.stdin.flush() # 确保数据被发送
# 读取子进程的输出
output, errors = proc.communicate(timeout=5) # communicate 会读取所有输出并等待进程结束
print(f"主脚本:收到子进程输出 -> {output.strip()}")
if errors:
print(f"主脚本:收到子进程错误 -> {errors.strip()}")
print(f"主脚本:子进程的最终返回码是 {proc.returncode}")
interactive_sub_script.py
import sys
import time
print("子脚本:我已启动,等待指令...")
# 循环读取来自主进程的输入
for line in sys.stdin:
command = line.strip()
print(f"子脚本:收到指令 '{command}'")
if command == "greet":
print("子脚本:你好,主进程!")
elif command == "exit":
print("子脚本:正在退出...")
break
else:
print("子脚本:未知指令")
sys.stdout.flush() # 确保输出被发送
print("子脚本:运行结束。")
sys.exit(0)
使用 import 语句 (在同一个程序内)
如果你的代码逻辑上是一个整体,只是想拆分成不同的模块来组织,那么应该使用 import。
module_a.py
def function_a():
print("模块 A 的函数被调用了")
return 42
main_module.py
# 导入整个模块
import module_a
# 现在可以调用模块中的函数
result = module_a.function_a()
print(f"从模块 A 得到的返回值是: {result}")
# 也可以导入特定函数
# from module_a import function_a
# result = function_a()
关键区别:
import:在同一个 Python 解释器进程内运行,两个模块共享全局变量(除非使用multiprocessing)。subprocess:创建一个全新的、独立的 Python 解释器进程,它有自己的内存空间和全局变量,与父进程完全隔离。
使用 os.system() (不推荐,仅作了解)
这是最老式的方法,简单但功能有限,不推荐在新代码中使用。
import os
print("主脚本开始运行...")
# 返回的是命令执行后的退出码,而不是输出
return_code = os.system("python sub_script.py")
print(f"子脚本执行完毕,退出码: {return_code}")
print("主脚本运行结束。")
缺点:
- 无法直接获取子脚本的输出(除非重定向到文件)。
- 不够灵活,错误处理能力差。
- 安全性较低,如果参数来自用户,容易产生命令注入漏洞。
总结与最佳实践
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
subprocess.run() |
现代、安全、灵活、功能强大、能轻松获取返回码和输出。 | 相比 os.system 稍显复杂。 |
绝大多数情况下的首选,执行独立脚本、传递参数、获取结果。 |
subprocess.Popen() |
功能最强大、支持异步、复杂交互(双向通信)。 | API 最复杂,需要手动管理进程。 | 需要与子进程进行实时交互、长时间运行的后台任务。 |
import |
简单、高效、共享内存和状态。 | 代码必须在同一个程序内,逻辑耦合。 | 将代码组织成模块和函数,逻辑上属于一个整体的应用。 |
os.system() |
极其简单,一行代码即可。 | 功能弱、不安全、无法获取输出。 | 快速执行一个简单的命令,不关心结果,仅用于兼容旧代码。 |
最终建议:
- 如果只是想运行另一个独立的
.py文件,并获取其执行结果或输出,请使用subprocess.run()。 - 如果你的代码逻辑上是一个整体,只是想拆分文件,请使用
import。 - 如果你需要与一个子进程进行复杂的、实时的交互,请使用
subprocess.Popen()。 - 避免使用
os.system(),除非你正在维护一个非常古老的代码库。
