关于python的subprocess库使用
subprocess 是 Python 标准库中用于创建和管理子进程的模块,提供了一个更强大且灵活的接口来执行外部命令。以下是它的常用方法及适用场景:
1. subprocess.run()
功能:执行外部命令并等待完成,返回一个 CompletedProcess 对象。
适用场景:简单的一次性命令执行,需要获取命令的返回码、输出结果或检查执行状态。
示例:
import subprocess
result = subprocess.run(["ls", "-l"], capture_output=True, text=True)
print("返回码:", result.returncode)
print("标准输出:", result.stdout)
print("错误输出:", result.stderr)
2. subprocess.Popen()
功能:创建一个新的子进程,返回一个 Popen 对象,允许更细粒度的控制(如异步执行、管道通信)。
适用场景:需要与子进程进行交互、实时处理输出或执行复杂的命令链。
示例:
import subprocess
process = subprocess.Popen(["ping", "google.com"], stdout=subprocess.PIPE)
for line in iter(process.stdout.readline, b''):
print(line.decode("utf-8").strip())
3. subprocess.check_call()
功能:执行命令并等待完成,若返回码非零则抛出 CalledProcessError。
适用场景:需要确保命令成功执行,否则终止程序。
示例:
import subprocess
try:
subprocess.check_call(["git", "clone", "https://github.com/repo.git"])
print("克隆成功!")
except subprocess.CalledProcessError as e:
print(f"克隆失败,错误码: {e.returncode}")
4. subprocess.check_output()
功能:执行命令并返回其标准输出(字符串),若返回码非零则抛出异常。
适用场景:需要获取命令的输出结果并进行后续处理。
示例:
import subprocess
output = subprocess.check_output(["python", "--version"], text=True)
print("Python 版本:", output.strip())
5. subprocess.getstatusoutput()
功能:(在 Python 3.5+ 中推荐使用 run())执行命令并返回一个元组 (returncode, output)。
适用场景:兼容旧代码或需要同时获取返回码和输出。
示例:
import subprocess
status, output = subprocess.getstatusoutput("ls -l")
print(f"状态码: {status}, 输出行数: {len(output.splitlines())}")
参数说明
shell=True:通过 shell 执行命令(支持管道、变量等),但存在安全风险(如命令注入)。stdout/stderr:指定输出流(如subprocess.PIPE、subprocess.DEVNULL)。text=True:以文本模式处理输入/输出(默认是二进制)。capture_output=True:捕获标准输出和错误输出。
适用场景总结
| 方法 | 同步/异步 | 输出捕获 | 错误处理 | 适用场景 |
|---|---|---|---|---|
subprocess.run() |
同步 | 支持 | 手动检查 | 简单命令执行 |
subprocess.Popen() |
异步 | 灵活控制 | 手动处理 | 复杂交互或实时处理 |
subprocess.check_call() |
同步 | 不支持 | 自动抛异常 | 必须成功执行的命令 |
subprocess.check_output() |
同步 | 自动捕获 | 自动抛异常 | 获取命令输出并处理 |
安全提示
- 避免在
shell=True时使用用户输入构建命令,防止命令注入。 - 优先使用
subprocess.run()或Popen的参数列表形式(如["ls", "-l"])而非字符串。
通过这些方法,subprocess 提供了强大的进程管理能力,适用于从简单脚本到复杂系统交互的各种场景。
应用场景版本
1. 我需要获取某个命令的执行结果,过程不需要交互
如果你只需要获取命令的执行结果,且过程不需要交互(即同步执行,等待命令完成后获取输出),推荐使用 subprocess.run() 或 subprocess.check_output()。具体选择取决于是否需要严格检查命令的返回状态:
1. 使用 subprocess.run()(推荐)
适用场景:需要完整控制命令执行,包括返回码、标准输出和错误输出,且允许命令失败(返回非零状态码)。
示例:
import subprocess
result = subprocess.run(
["ls", "-l"], # 命令及参数列表
capture_output=True, # 捕获输出
text=True, # 以文本模式处理
check=False # 不强制检查返回码(默认)
)
print("返回码:", result.returncode)
print("标准输出:", result.stdout)
print("错误输出:", result.stderr)
优点:
- 功能全面,支持捕获输出、检查返回码、超时控制等。
- 代码可读性高,参数明确。
2. 使用 subprocess.check_output()
适用场景:只需获取命令的标准输出,且要求命令必须成功执行(返回非零状态码时抛出异常)。
示例:
import subprocess
try:
output = subprocess.check_output(
["python", "--version"], # 命令及参数列表
text=True # 以文本模式返回
)
print("Python 版本:", output.strip())
except subprocess.CalledProcessError as e:
print(f"命令执行失败,错误码: {e.returncode}")
print(f"错误信息: {e.stderr}")
优点:
- 代码简洁,直接返回输出字符串。
- 自动处理错误,确保命令成功执行。
对比与选择建议
| 方法 | 返回类型 | 命令失败时行为 | 推荐场景 |
|---|---|---|---|
subprocess.run() |
CompletedProcess 对象 |
返回非零 returncode |
需要完整控制(如获取返回码、错误信息) |
subprocess.check_output() |
字符串 | 抛出 CalledProcessError |
只需输出结果,且命令必须成功执行 |
安全提示
- 避免使用
shell=True(尤其是命令包含用户输入时),防止命令注入。 - 若需执行复杂命令(如含管道),推荐使用参数列表形式(如
["cmd1", "|", "cmd2"]需拆分为多个Popen)。
综上所述,优先使用 subprocess.run(),因为它更灵活且易于扩展,适合大多数场景。
我需要获取某个命令执行过程中输出的信息以及执行结果,执行状态
若你需要实时获取命令执行过程中的输出信息,同时跟踪执行状态(如返回码),推荐使用 subprocess.Popen 配合 管道(PIPE) 和 异步读取。这种方式能在命令执行期间实时捕获输出,而非等待命令结束后一次性获取结果。
推荐方案:subprocess.Popen + 实时输出捕获
import subprocess
def run_command(command):
# 创建子进程并捕获标准输出和错误输出
process = subprocess.Popen(
command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True # 以文本模式处理输出
)
# 实时读取并打印标准输出
for line in iter(process.stdout.readline, ''):
print("标准输出:", line.strip())
# 等待命令执行完成,获取返回码和错误输出
returncode = process.wait()
stderr = process.stderr.read()
# 检查错误输出
if stderr:
print("错误输出:", stderr.strip())
return {
"returncode": returncode,
"success": returncode == 0,
"stderr": stderr
}
# 使用示例
result = run_command(["ping", "-c", "3", "google.com"])
print("执行结果:", result)
关键特性说明
-
实时输出捕获:
- 通过
iter(process.stdout.readline, '')逐行读取标准输出,确保在命令执行期间实时显示信息。
- 通过
-
执行状态获取:
process.wait()等待命令完成并返回退出状态码。returncode为0表示成功,非零表示失败。
-
错误处理:
- 单独捕获标准错误(
stderr),区分正常输出和错误信息。
- 单独捕获标准错误(
适用场景
- 长时间运行的命令(如安装程序、构建过程),需要实时查看进度。
- 需要根据输出动态调整逻辑(如监控日志中的特定关键字)。
- 交互式命令(如
git clone、docker build),需实时反馈。
其他可选方案对比
| 方法 | 实时输出 | 执行状态 | 适用场景 |
|---|---|---|---|
subprocess.Popen |
✅ | ✅ | 需要实时监控和状态跟踪 |
subprocess.run() |
❌ | ✅ | 一次性获取结果,无需实时输出 |
subprocess.check_output() |
❌ | ✅(仅成功时) | 仅需成功后的输出,失败即抛异常 |
扩展:异步读取多流
若需同时处理标准输出和错误输出,且不阻塞主线程,可使用 asyncio(Python 3.7+):
import asyncio
import subprocess
async def read_stream(stream, callback):
while True:
line = await stream.readline()
if line:
callback(line.decode("utf-8").strip())
else:
break
async def run_command_async(command):
process = await asyncio.create_subprocess_exec(
*command,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
# 异步读取标准输出和错误输出
tasks = [
asyncio.create_task(read_stream(process.stdout, lambda x: print("stdout:", x))),
asyncio.create_task(read_stream(process.stderr, lambda x: print("stderr:", x)))
]
# 等待所有任务完成并获取返回码
await asyncio.gather(*tasks)
returncode = await process.wait()
return {"returncode": returncode, "success": returncode == 0}
# 使用示例
asyncio.run(run_command_async(["ping", "-c", "3", "google.com"]))
这种方式通过协程实现非阻塞读取,适合需要更复杂交互的场景。
我需要在执行命令的时候,输入某些值,并根据返回,对应的再输入某些值,同时也关注执行结果
若你需要与命令进行交互式通信(如输入密码、确认选项等),并根据命令的实时输出来动态调整后续输入,可以使用 subprocess.Popen 结合 标准输入/输出流控制。这种方式允许你在命令执行期间动态发送输入并捕获输出。
推荐方案:subprocess.Popen + 交互式输入输出控制
import subprocess
def interactive_command(command, inputs):
# 创建子进程,启用标准输入/输出/错误流的管道
process = subprocess.Popen(
command,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True # 以文本模式处理输入/输出
)
outputs = []
current_input_index = 0
while True:
# 实时读取输出
output_line = process.stdout.readline()
if not output_line and process.poll() is not None:
break # 命令执行完毕且无更多输出
outputs.append(output_line.strip())
print("命令输出:", output_line.strip())
# 根据输出内容决定是否需要发送输入
for trigger, response in inputs:
if trigger in output_line:
if current_input_index < len(inputs):
input_value, _ = inputs[current_input_index]
print("发送输入:", input_value)
process.stdin.write(input_value + "\n")
process.stdin.flush()
current_input_index += 1
break
# 等待命令执行完成,获取返回码和错误信息
returncode = process.wait()
stderr = process.stderr.read()
return {
"returncode": returncode,
"success": returncode == 0,
"outputs": outputs,
"stderr": stderr
}
# 使用示例:模拟交互式登录
result = interactive_command(
["python", "-c", "print('请输入用户名:'); user=input(); print('请输入密码:'); pw=input(); print(f'登录成功: {user}')"],
[("请输入用户名:", "admin"), ("请输入密码:", "password123")]
)
print("执行结果:", result)
关键特性说明
-
交互式输入控制:
- 通过
process.stdin.write()向命令发送输入。 - 使用
inputs参数定义触发条件(如检测到特定提示)和对应的响应。
- 通过
-
实时输出捕获与解析:
- 逐行读取命令的标准输出,并检查是否包含预定义的触发字符串。
- 根据匹配结果动态发送相应输入。
-
状态跟踪:
- 捕获返回码和错误信息,确保全面了解命令执行状态。
适用场景
- 需要输入密码、确认选项等交互的命令(如
ssh、sudo、安装向导)。 - 基于命令输出动态调整后续输入的场景(如根据提示选择不同选项)。
注意事项
-
缓冲问题:某些命令可能会缓冲输出,导致无法实时看到内容。可尝试:
# 使用 unbuffer 命令(需安装 expect 包)禁用缓冲 process = subprocess.Popen( ["unbuffer"] + command, # ... 其他参数保持不变 ) -
复杂交互:对于更复杂的交互场景(如正则表达式匹配、超时处理),可考虑使用
pexpect库:import pexpect child = pexpect.spawn("python -c 'print(\"请输入用户名:\"); user=input()'") child.expect("请输入用户名:") child.sendline("admin") child.expect(pexpect.EOF) print(child.before.decode()) -
安全风险:避免在代码中硬编码敏感信息(如密码),建议通过环境变量或用户输入动态获取。
扩展:超时处理与非阻塞读取
若需要处理可能卡住的命令或设置响应超时,可结合 select 模块实现非阻塞读取:
import subprocess
import select
import time
def run_with_timeout(command, inputs, timeout=30):
process = subprocess.Popen(
command,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
bufsize=1 # 行缓冲
)
start_time = time.time()
current_input = 0
outputs = []
while True:
# 检查超时
if time.time() - start_time > timeout:
process.terminate()
return {"success": False, "error": "命令执行超时"}
# 非阻塞读取输出
readable, _, _ = select.select([process.stdout], [], [], 0.1)
if readable:
line = process.stdout.readline()
if not line:
break
outputs.append(line.strip())
print("输出:", line.strip())
# 根据输出发送输入
if current_input < len(inputs) and inputs[current_input][0] in line:
process.stdin.write(inputs[current_input][1] + "\n")
process.stdin.flush()
current_input += 1
# 检查命令是否完成
if process.poll() is not None:
break
returncode = process.wait()
return {
"returncode": returncode,
"success": returncode == 0,
"outputs": outputs
}
这种方式能更好地控制交互式命令的执行流程,同时确保代码健壮性。

浙公网安备 33010602011771号