Python subprocess库详解:从基础命令执行到进程间通信
本文深入解析Python中subprocess库的核心用法,涵盖从简单的run命令执行到复杂的Popen进程控制。详细讲解如何通过设置stdout和stderr参数捕获输出,利用PIPE实现单项与双向通信,以及构建命令管道链。通过具体代码示例,展示如何处理错误码、抑制输出及与系统标准输入交互,帮助开发者掌握高效创建和管理附加进程的技术。
前言
subprocess库提供了一套应用程序编程接口(API)用于创建子进程并与之进行交互。这一机制在需要运行生成或消费文本数据的程序时尤为关键,因为它允许通过新进程的标准输入(stdin)和标准输出(stdout)通道进行双向数据传递。
本文将详细阐述如何利用Python的subprocess库来创建和管理附加进程。
run(运行外部命令)
subprocess库的设计初衷之一是替代旧式的os.system()和os.spawnv()等函数。接下来,我们将演示如何使用subprocess库执行外部命令,而不依赖os.system()。具体示例如下:
import subprocess
completed = subprocess.run('whoami')
print(completed.returncode)
在此示例中,我们执行了Windows系统中常用的whoami命令,该命令用于返回当前登录用户的名称。运行后的输出结果如下:

这里,我们调用subprocess.run来启动子进程执行Windows命令。该函数返回一个CompletedProcess实例,其中包含了关于该进程的详细信息。属性returncode表示子进程的退出状态码。通常情况下,退出状态码为0表示进程成功执行;若为负值-N,则表明该子进程是被信号N强制终止的。
该函数还包含多个参数,例如shell。其默认值为False,表示直接执行命令;如果将其显式设置为True,则会创建一个中间shell进程,由该shell进程负责运行指定的命令。
import subprocess
completed = subprocess.run('echo 123',shell=True)
print(completed.returncode)
例如,这里我们打印数字123。
该库还提供了一个call()函数。当subprocess.run中的check参数未设置时(默认值为False),其行为等价于调用call()函数。
对于由run()函数启动的进程,其标准输入和输出通道默认绑定到父进程的对应通道。这意味着调用程序无法直接捕获命令的输出结果。不过,我们可以通过为stdout和stderr参数传入subprocess.PIPE来捕获这些输出,以便后续处理。
import subprocess
completed = subprocess.run('whoami',stdout=subprocess.PIPE)
print(completed.returncode)
print(len(completed.stdout))
print(completed.stdout.decode('UTF-8'))
运行上述代码后的效果如下:

如果在run()函数中设置check=True且将stdout设为PIPE,其效果等价于调用check_output()函数。
通过Shell返回消息
本例演示如何通过子shell运行命令,并在命令返回错误码并退出之前,将详细信息输出到控制台。具体实例如下:
import subprocess
try:
completed = subprocess.run(
'echoa 123',
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE, )
except subprocess.CalledProcessError as err:
print("ERROR:", err)
else:
print("else")
print(completed.returncode)
print(len(completed.stdout))
print(completed.stdout.decode('UTF-8'))
print(len(completed.stderr))
print(completed.stderr.decode('gbk'))
运行后的效果如下:

这里我们输入了一个错误的命令。由于命令错误,并未输出命令的执行结果,completed.stdout位于0和64之间,内容为空。而命令将错误消息返回了。这是因为我们将stdout与stderr设置为subprocess.PIPE,表明这些通道是开放的。这样我们才能获取子shell运行的结果以及所运行的错误提示。(读者可以将命令改正确后可以发现错误消息没有了,正确执行结果会输出。这就是subprocess库创建进程的通信机制)
需要注意的是,如果需要抑制输出效果,可以将stdout与stderr设置为subprocess.DEVNULL。不过改了之后,上面代码肯定会报错,因为管道关闭,通信也就关闭了。也就是没有这些参数了。
直接处理管道
subprocess库中还有一个非常重要的类Popen,它是构建其他高层API的底层接口,对于处理更复杂的进程交互非常有用。
例如,run()、call()、check_call()和check_output()函数都是Popen类的封装器。直接使用Popen可以更好地控制命令的运行方式以及输入和输出流的处理。Popen的构造函数利用参数建立新进程,使父进程可以通过管道与之通信。
下面,我们来分别介绍进程间通信的方式。
与进程的单项通信
要运行一个进程并读取它的所有输出,可以设置stdout为PIPE并调用communicate()函数。示例如下:
import subprocess
prc = subprocess.Popen('whoami', stdout=subprocess.PIPE)
stdout_value = prc.communicate()[0].decode('utf-8')
print(repr(stdout_value))
如上面代码所示,Popen会在内部管理数据读取。运行之后,效果如下:

如果你需要调用一个管道,并完成写数据的操作,可以设置stdin为PIPE。
import subprocess
prc = subprocess.Popen(["cmd", "/c", 'type', '-'], stdin=subprocess.PIPE)
prc.communicate('stdin'.encode('UTF-8'))
与进程的双向通信
要完成进程的双向通信,可以直接将stdin与stdout都设置为PIPE即可。示例如下:
import subprocess
cmd = "cmd /c type E:/Project/debug.log"
cmd.encode('utf-8')
prc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
msg = 'stdin'.encode('UTF-8')
stdout_value = prc.communicate(msg)[0].decode('utf-8')
print(repr(stdout_value))
至于如果命令行错误需要捕获错误消息,可以直接将stderr也设置为PIPE。
连接管道段
在Linux系统中,我们可以将多个命令连接成一个管线,即可以把它们的输入输出串联在一起。通过Popen我们也可以完成类似的操作,只需要将一个Popen实例的stdout属性被用左管线中下一个Popen实例的stdin参数即可。至于最后肯定还是要设置为PIPE,毕竟我们还是要获取多个管道段消息结果,示例如下:
import subprocess
cmd1 = "cmd /c type E:/Project/debug.log"
proc1 = subprocess.Popen(cmd1, stdout=subprocess.PIPE, encoding='utf-8')
cmd2 = "tree /F | findstr 拒绝访问"
proc2 = subprocess.Popen(cmd1, stdout=subprocess.PIPE, stdin=proc1.stdout, encoding='utf-8')
result = proc2.stdout
for line in result:
print(line.decode('utf-8').strip())
sys的命令交互
在我们学习Python时,一般使用input()进行用户输入数据。但是其实sys库也可以进行输入输出判断,但它涉及的是进程间的交互,示例如下:
import sys
sys.stderr.write('开始\n')
sys.stderr.flush()
while True:
next_line = sys.stdin.readline()
sys.stderr.flush()
if next_line.strip() == "9599":
break
sys.stdout.write(next_line)
sys.stdout.flush()
sys.stderr.write('结束\n')
sys.stderr.flush()
运行之后,效果如下:

到此这篇关于Python中使用subprocess库创建附加进程的文章就介绍到这了,更多相关Python附加进程内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













