Python 的 subprocess
模块用于创建和管理子进程,执行外部命令或脚本。它提供了灵活的方法来启动进程、与进程交互、捕获输出,并处理错误。以下是 subprocess
模块的核心方法及其实际场景的详细说明。
一、subprocess
核心方法
1. subprocess.run()
功能:执行命令并等待其完成,返回 CompletedProcess
对象。
参数:
args
:命令(字符串或列表形式,推荐列表避免安全问题)。stdout
/stderr
:输出管道(如subprocess.PIPE
或文件对象)。check
:若为True
,命令失败时抛出CalledProcessError
。text
:输入/输出以字符串形式处理(Python 3.7+ 支持)。
示例场景:运行 Shell 命令并捕获输出。
import subprocess
result = subprocess.run(
["ls", "-l"], # 推荐使用列表形式(避免 shell 注入)
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True # 输出为字符串而非字节
)
if result.returncode == 0:
print("命令成功执行:\n", result.stdout)
else:
print("命令失败:\n", result.stderr)
2. subprocess.Popen()
功能:更底层的进程创建接口,支持异步操作、实时交互和复杂管道管理。
关键方法:
poll()
:检查进程是否终止。communicate(input)
:发送输入并等待进程结束,返回(stdout, stderr)
。terminate()
/kill()
:终止进程。
示例场景:实时读取进程输出并动态处理。
import subprocess
# 启动进程(执行 ping 命令)
proc = subprocess.Popen(
["ping", "google.com"],
stdout=subprocess.PIPE,
text=True
)
# 逐行读取输出
while True:
line = proc.stdout.readline()
if not line and proc.poll() is not None:
break
if line:
print(line.strip())
print("进程退出码:", proc.returncode)
3. 旧版便捷函数
subprocess.call()
:执行命令并返回退出码。subprocess.check_call()
:若命令失败则抛出异常。subprocess.check_output()
:执行命令并返回输出(失败时抛出异常)。
示例场景:快速执行命令并检查结果。
import subprocess
# 检查命令是否成功
try:
subprocess.check_call(["git", "commit", "-m", "Update code"], check=True)
except subprocess.CalledProcessError as e:
print("Git 提交失败:", e)
# 获取命令输出
output = subprocess.check_output(["date"], text=True)
print("当前时间:", output)
二、实际应用场景
场景 1:执行 Shell 脚本并传递参数
需求:调用脚本处理文件,传递输入路径和输出路径。
import subprocess
script_path = "process_data.sh"
input_file = "data.csv"
output_file = "result.csv"
result = subprocess.run(
[script_path, input_file, output_file],
capture_output=True,
text=True
)
if result.returncode != 0:
print("脚本执行失败:", result.stderr)
else:
print("处理完成:", result.stdout)
场景 2:通过管道传递输入
需求:向进程动态发送输入(如自动化交互式命令行工具)。
import subprocess
# 启动 Python 解释器进程
proc = subprocess.Popen(
["python3"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
# 发送代码并获取输出
commands = """
print("Hello, subprocess!")
x = 1 + 2
print(f"计算结果:{x}")
"""
stdout, stderr = proc.communicate(commands)
print("输出结果:\n", stdout)
场景 3:并行执行多个命令
需求:同时运行多个耗时任务并收集结果。
import subprocess
from concurrent.futures import ThreadPoolExecutor
def run_command(cmd):
result = subprocess.run(cmd, capture_output=True, text=True)
return result.stdout
commands = [
["sleep", "3"], # 模拟耗时任务
["echo", "Hello"],
["ls", "/tmp"]
]
with ThreadPoolExecutor(max_workers=3) as executor:
results = list(executor.map(run_command, commands))
for res in results:
print(res)
场景 4:重定向输出到文件
需求:将命令的输出写入日志文件。
import subprocess
with open("output.log", "w") as f:
subprocess.run(
["python3", "-m", "http.server", "8000"],
stdout=f, # 标准输出写入文件
stderr=subprocess.STDOUT # 错误输出合并到 stdout
)
三、关键注意事项
1. 避免 shell=True
的安全风险
- 不安全写法:
subprocess.run(f"rm -rf {user_input}", shell=True) # 可能被注入恶意命令
- 安全写法:
subprocess.run(["rm", "-rf", sanitized_user_input]) # 参数列表形式
2. 处理超时
通过 timeout
参数限制命令执行时间:
try:
subprocess.run(["sleep", "10"], timeout=5)
except subprocess.TimeoutExpired:
print("命令执行超时!")
3. 跨平台兼容性
- 路径分隔符:使用
os.path
处理路径分隔符(如os.path.join("dir", "file.txt")
)。 - 命令差异:Windows 和 Linux 的命令不同(如
dir
vsls
)。
四、总结
subprocess
模块的核心方法:
run()
:简单执行命令并获取结果。Popen()
:底层控制,支持异步和交互。- 旧版函数:快速调用(
check_call
、check_output
)。
实际场景:
- 执行脚本或系统命令。
- 动态交互式输入输出。
- 并行任务处理。
遵循最佳实践(如避免 shell=True
、处理超时),可以安全高效地管理子进程。