深入理解python中的subprocess模块

CSDN 2024-10-04 16:05:01 阅读 79

🚀 个人主页:xmp65535

🚀 专栏:python技术专栏

目录

前言

一、subprocess模块概述

为什么要使用subprocess模块?

二、subprocess的常用函数

1. subprocess.run()

2. subprocess.Popen()

3. subprocess.call()

4. subprocess.check_call()

5. subprocess.check_output()

6. subprocess.getstatusoutput()

7. subprocess.getoutput()

三、subprocess模块的高级用法

1. 与子进程的流交互

2. 使用管道

3. 处理超时


前言

在实际开发过程中,我们经常会遇到需要从Python脚本中调用外部程序或脚本的场景。这时候,Python的<code>subprocess模块就显得尤为重要。它是Python用来生成子进程、连接到它们的输入/输出/错误管道,以及获取它们的返回码的一个模块。本文将带你深入了解subprocess模块,学会如何高效地使用它来扩展你的Python脚本功能。

一、subprocess模块概述

subprocess模块是Python标准库的一部分,它为创建和管理子进程提供了强大的接口。在Python 2.4中引入以取代旧的模块(如os.systemos.spawn*等),并提供了更复杂的进程管理功能,比如超时控制、I/O管道通信等。

为什么要使用subprocess模块?

安全性:与os.system相比,subprocess避免了shell注入攻击的风险。灵活性subprocess可以与子进程的stdin、stdout和stderr流进行交互。功能丰富:它支持复杂的系统调用,如管道和重定向。

二、subprocess的常用函数

1. subprocess.run()

subprocess.run() 是 Python 3.5 引入的一个高级接口,用于替代旧的 subprocess.call() 和 subprocess.check_output() 等函数。它可以执行命令并等待其完成,然后返回一个 CompletedProcess 对象,包含执行结果的信息,如返回码、标准输出和标准错误等。subprocess.run() 提供了更多的参数选项,如可以指定输入数据、设置超时时间、指定工作目录等。

参数说明

args:这是命令和参数的列表,其中命令是列表的第一个元素。如果 shell 参数设置为 True,args必须是一个字符串

stdin:可以是 None、一个已打开的文件描述符、一个现有的文件对象,或者 subprocess.PIPE。用于指定子进程的标准输入。

input:如果 stdin 参数是 PIPE,这个参数可以被用来传递一个字符串到子进程的标准输入。

stdoutstderr:这些参数与 stdin 类似,但它们控制的是标准输出和标准错误输出。它们也可以被设置为 None、一个文件描述符、一个现有的文件对象,或者 subprocess.PIPE

capture_output:如果设置为 Truestdoutstderr 将会被捕获。这相当于设置 stdout=subprocess.PIPEstderr=subprocess.PIPE

shell:如果为 True,指定的命令将通过 shell 执行。

cwd:如果指定,子进程的当前工作目录将被改变到 cwd

timeout:如果指定,且执行时间超过了这个值(以秒计),将会抛出 subprocess.TimeoutExpired 异常。

check:如果设置为 True,并且进程以非零状态码退出,将会抛出 subprocess.CalledProcessError 异常。

encodingerrors:指定如何解码从子进程输出的字节。只有当 stdoutstderr 被捕获时才有效。

text:一个简写的标志,用于设置 encoding='utf-8'code> 和 universal_newlines=True,它会将 stdoutstderr 的输出作为字符串处理。

env:指定子进程的环境变量。如果为 None,使用父进程的环境变量。

universal_newlines:如果为 Truestdinstdoutstderr 将会作为文本流处理,类似于 text=True

**other_popen_kwargs:你可以提供其他的关键字参数,这些参数将直接传递给 Popen 构造函数。

示例

import subprocess

try:

result = subprocess.run(['ls', '-l', '/nonexistent'], capture_output=True, text=True, check=True)

print(result.stdout)

except subprocess.CalledProcessError as e:

print('Failed to run command:', e)

2. subprocess.Popen()

subprocess.Popen() 是一个更底层的接口,用于在新的进程中执行命令。它返回一个 Popen 对象,可以用于控制和管理子进程的执行过程,如向子进程发送输入数据、读取子进程的输出、等待子进程结束等。相比于其他函数,subprocess.Popen() 提供了更多的灵活性和控制。

参数说明

args: 和 run 函数相同。bufsize:缓冲区大小,-1 表示使用系统默认缓冲区大小,0 表示不使用缓冲区,1 表示行缓冲。executable:如果指定,将使用这个可执行文件来替换要执行的程序。stdinstdoutstderrshellcwd:与 run 函数相同。preexec_fn:仅在 Unix 系统上有效)是一个可调用对象,它将在子进程运行之前被调用。close_fds:如果为 True(默认值),在子进程中除了 012 之外所有的文件描述符都将被关闭。env:用于指定环境变量。

shell:如果这个参数为 True,指定的命令将通过 shell 执行。

cwd:如果指定,子进程的工作目录将改变到 cwd

env:用于指定子进程的环境变量。如果为 None,子进程会继承父进程的环境变量。

universal_newlines:(现在推荐使用 text 参数)如果为 Truestdinstdoutstderr 将作为文本流(字符串)而不是字节流。

startupinfocreationflags:这两个参数仅在 Windows 系统上有效,用于控制子进程的创建方式。

restore_signals:(仅在 Unix 系统上有效)如果为 True,在执行新程序前,将会恢复 Python 的信号处理为默认值。

start_new_session:(仅在 Unix 系统上有效)如果为 True,子进程将会在新的会话中启动。

pass_fds:(仅在 Unix 系统上有效)要传递给子进程的文件描述符。

encodingerrors:这些参数用于设置在解码和编码文本数据时使用的编码和错误处理方式。

text:一个用于设置编码为 "utf-8"universal_newlines=True 的简化参数,这将会让 stdoutstderr 的输出作为字符串处理。

示例

import subprocess

process = subprocess.Popen(['ping', '-c', '4', 'example.com'], stdout=subprocess.PIPE, text=True)

stdout, stderr = process.communicate()

print(stdout)

Popen 类有一些其他的方法供进阶使用,例如 poll(), wait(), communicate() 等。

poll():检查子进程是否已经结束,返回 returncode 属性。wait():等待子进程结束,并返回 returncode 属性。communicate(input=None, timeout=None):与子进程进行交互,发送数据到 stdin,并从 stdout 和 stderr 读取数据,然后等待子进程结束。

示例

import subprocess

process = subprocess.Popen(['sleep', '2'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)

print(process.poll()) # None,因为进程还未结束

output, error = process.communicate() # 等待进程结束,并获取输出

print(process.poll()) # 返回码,通常为0,表示成功

subprocess.PIPE

subprocess.PIPE 可以被用作 Popen 函数的 stdin/stdout/stderr 参数的值,它表示需要创建一个新的管道到子进程。

示例

import subprocess

# 创建新的管道,捕获输出

with subprocess.Popen(['ls', '-l'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc:

out, err = proc.communicate()

print(out.decode())

3. subprocess.call()

这个函数用于执行指定的命令,等待命令执行完成,并返回命令的返回码。

参数 基本上与 subprocess.run() 相同。

示例

import subprocess

return_code = subprocess.call(["echo", "Hello, World!"])

print("Command returned:", return_code)

4. subprocess.check_call()

这个函数与 call() 类似,但如果执行的命令返回非0退出码,则会抛出 CalledProcessError

参数subprocess.call() 相同。

示例

import subprocess

try:

subprocess.check_call(["false"]) # 这将抛出异常,因为 'false' 命令的退出码不为0

except subprocess.CalledProcessError as e:

print('Error:', e)

5. subprocess.check_output()

subprocess.check_output() 用于执行命令并返回其标准输出。如果命令执行失败或返回非零的返回码,则会抛出 CalledProcessError 异常。

参数 基本上与 subprocess.run() 相同。

示例

import subprocess

try:

output = subprocess.check_output(["echo", "Hello, World!"])

print(output.decode()) # 输出需要解码

except subprocess.CalledProcessError as e:

print('Error:', e)

6. subprocess.getstatusoutput()

这个函数在 Unix 上执行带有{ cmd ; } 2>&1的shell命令,并返回 (status, output) 元组。

示例

import subprocess

status, output = subprocess.getstatusoutput('ls /nonexistent')

print('Status:', status)

print('Output:', output)

这个命令会尝试列出一个不存在的目录,因此它将返回一个错误状态和错误信息。

7. subprocess.getoutput()

这个函数用于执行命令并获取它的输出(stdout),不返回返回码。

示例

import subprocess

output = subprocess.getoutput('echo Hello, World!')

print('Output:', output)

这个命令会打印 Hello, World! 到stdout,并捕获这个输出。

三、subprocess模块的高级用法

1. 与子进程的流交互

subprocess 模块允许你启动新的进程,连接到它们的输入/输出/错误管道,并获取它们的返回码。这个模块提供了很多与子进程进行交互的功能,Popen 类是其中的核心。使用 Popen 构造函数,你可以指定进程的输入、输出、错误管道,以及许多其他的选项。

例如,你可以使用 Popen 类并将 stdinstdout,和 stderr 参数设置为 subprocess.PIPE,这样就可以与子进程的输入和输出流进行交互:

import subprocess

# 启动进程

proc = subprocess.Popen(['sort'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

# 向子进程发送数据并获取输出

output, errors = proc.communicate(input=b'one\ntwo\nthree\nfour\nfive\n')

# 打印排序后的输出数据

print(output.decode()) # 输出将会是排序后的列表

在这个例子中,communicate() 方法用于发送数据到子进程的 stdin,并从 stdout 和 stderr 获取数据。

2. 使用管道

在 Unix 和 Windows 系统中,可以使用 subprocess 模块创建管道。这通常是通过将一个进程的 stdout 与另一个进程的 stdin 相连来完成的。

import subprocess

# 创建一个管道:grep通过ps获取的进程列表

ps = subprocess.Popen(('ps', 'aux'), stdout=subprocess.PIPE)

grep = subprocess.Popen(('grep', 'python'), stdin=ps.stdout, stdout=subprocess.PIPE)

ps.stdout.close() # 允许ps释放资源

output = grep.communicate()[0]

print(output.decode())

在这个例子中,ps 命令的输出被直接连接到了 grep 命令的输入。我们通过关闭 ps.stdout 来确保 ps 可以接收到 SIGPIPE 信号,如果 grep 先完成。

3. 处理超时

subprocess.run() 函数包括一个 timeout 参数,可以用来限制子进程的运行时间。如果超时,将会抛出一个 TimeoutExpired 异常。

import subprocess

try:

# 运行命令,最多允许执行1秒

subprocess.run(['sleep', '2'], timeout=1)

except subprocess.TimeoutExpired:

print('The subprocess did not finish within the timeout period!')

在这个例子中,我们尝试运行一个会睡眠2秒的子进程,但是设置了1秒的超时。因为子进程执行时间超过了超时设置,所以 TimeoutExpired 异常被抛出。



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。