第19章-子流程模块

这个 子过程 模块使开发人员能够从Python启动进程或程序。换句话说,您可以使用子流程模块启动应用程序并将参数传递给它们。在python 2.4中添加了子进程模块,以替换 os 由os.popen、os.spawn和os.system调用的模块集,以及替换popen2和旧的 命令 模块。我们将研究子流程模块的以下方面:

  • 调用函数

  • 波本班

  • 如何与衍生过程通信

我们开始吧!

调用函数

子进程模块提供一个名为 call .此函数允许您调用另一个程序,等待命令完成,然后返回返回代码。它接受一个或多个参数以及以下关键字参数(及其默认值):stdin=none、stdout=none、stderr=none、shell=false。

让我们来看一个简单的例子:

>>> import subprocess
>>> subprocess.call("notepad.exe")
0

如果您在Windows机器上运行这个程序,您应该看到记事本打开了。您将注意到idle等待您关闭记事本,然后返回代码零(0)。这意味着它成功地完成了。如果你收到除了零以外的任何东西,那通常意味着你有某种错误。

通常,当您调用这个函数时,您需要将结果返回代码赋给一个变量,这样您就可以检查它是否是您期望的结果。让我们这样做:

>>> code = subprocess.call("notepad.exe")
>>> if code == 0:
        print("Success!")
    else:
        print("Error!")
Success!

如果运行此代码,您将看到它打印出来 成功! 到屏幕。这个 call 方法还接受要传递给正在执行的程序的参数。让我们看看它是如何工作的:

>>> code = subprocess.call(["ping", "www.yahoo.com"])

Pinging ds-any-fp3-real.wa1.b.yahoo.com [98.139.180.149] with 32 bytes of data:
Reply from 98.139.180.149: bytes=32 time=66ms TTL=45
Reply from 98.139.180.149: bytes=32 time=81ms TTL=45
Reply from 98.139.180.149: bytes=32 time=81ms TTL=45
Reply from 98.139.180.149: bytes=32 time=69ms TTL=45

Ping statistics for 98.139.180.149:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 66ms, Maximum = 81ms, Average = 74ms
>>> code
0

您会注意到在这个例子中,我们传递的是一个参数列表。列表中的第一项是我们要调用的程序。列表中的任何其他内容都是要传递给该程序的参数。在这个例子中,我们对雅虎的网站执行ping。您会注意到返回代码为零,所以一切都已成功完成。

您还可以使用操作系统的 .这确实为流程增加了一个抽象级别,并增加了安全问题的可能性。下面是关于这个问题的python文档的官方警告:

Executing shell commands that incorporate unsanitized input from an untrusted source makes a program vulnerable to shell injection, a serious security flaw which can result in arbitrary command execution. For this reason, the use of shell=True is strongly discouraged in cases where the command string is constructed from external input.

通常的建议是,如果外部进程或人员可以修改调用的参数,则不要使用它。如果你自己对某个东西进行硬编码,那么这就没什么关系了。

波本班

这个 波彭 类在新进程中执行子程序。不同于 call 方法,它不会等待被调用进程结束,除非使用 wait 方法。让我们尝试在popen中运行记事本,看看是否可以检测到差异:

>>> program = "notepad.exe"
>>> subprocess.Popen(program)
<subprocess.Popen object at 0x01EE0430>

在这里,我们创建一个名为 程序 并指定“notepad.exe”的值。然后我们把它传给波本班。当你运行这个时,你会看到它立即返回 子进程.popen对象 被调用的应用程序执行。让我们让Popen等待程序完成:

>>> program = "notepad.exe"
>>> process = subprocess.Popen(program)
>>> code = process.wait()
>>> print(code)
0

当你在 IDLE 时这样做,记事本将弹出,可能在 IDLE 会话之前。把记事本移开,但不要关闭它!你需要告诉你的过程 wait 因为你不能得到返回码。一旦你输入了这一行,关闭记事本并打印出代码。或者您可以将所有这些代码放到保存的python文件中并运行它。

注意,使用 wait 当进程生成足够的输出以阻塞管道时,如果使用stdout/stderr=pipe命令,方法可能会导致子进程死锁。您可以使用 沟通 缓解这种情况的方法。我们将在下一节中讨论这个方法。

现在让我们尝试使用多个参数运行popen:

>>> subprocess.Popen(["ls", "-l"])
<subprocess.Popen object at 0xb7451001>

如果您在Linux中运行此代码,您将看到它打印出popen对象消息,然后列出您在其中运行此代码的任何文件夹的权限和内容。也可以将shell参数与popen一起使用,但对popen的注意事项与对call方法的注意事项相同。

学习沟通

有几种方法可以与您调用的流程进行通信。我们只关注如何使用子流程模块 沟通 方法。让我们看看:

args = ["ping", "www.yahoo.com"]
process = subprocess.Popen(args,
                           stdout=subprocess.PIPE)

data = process.communicate()
print(data)

在这个代码示例中,我们创建一个 args 变量来保存参数列表。然后我们将标准输出(stdout)重定向到我们的子流程,以便我们可以与它通信。这个 沟通 方法本身允许我们与刚刚生成的过程进行通信。我们实际上可以使用这个方法将输入传递给流程。但在这个例子中,我们只是使用通信从标准输出读取数据。您将注意到,当您运行此代码时,通信将等待进程完成,然后返回包含stdout和stderr中的内容的两元素元组。以下是我的跑步结果:

('Pinging ds-any-fp3-real.wa1.b.yahoo.com [98.139.180.149] with 32 bytes of data:
Reply from 98.139.180.149: bytes=32 time=139ms TTL=45
Reply from 98.139.180.149: bytes=32 time=162ms TTL=45
Reply from 98.139.180.149: bytes=32 time=164ms TTL=45
Reply from 98.139.180.149: bytes=32 time=110ms TTL=45
Ping statistics for 98.139.180.149:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 110ms, Maximum = 164ms, Average = 143ms
', None)

有点难看。让我们把结果打印成更可读的格式,好吗?

import subprocess

args = ["ping", "www.yahoo.com"]
process = subprocess.Popen(args, stdout=subprocess.PIPE)

data = process.communicate()
for line in data:
    print(line)

如果运行此代码,屏幕上会显示如下内容:

Pinging ds-any-fp3-real.wa1.b.yahoo.com [98.139.180.149] with 32 bytes of data:
Reply from 98.139.180.149: bytes=32 time=67ms TTL=45
Reply from 98.139.180.149: bytes=32 time=68ms TTL=45
Reply from 98.139.180.149: bytes=32 time=70ms TTL=45
Reply from 98.139.180.149: bytes=32 time=69ms TTL=45

Ping statistics for 98.139.180.149:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 67ms, Maximum = 70ms, Average = 68ms

None

最后一行表示“无”是stderr的结果,这意味着没有错误。

总结

此时,您已经掌握了有效使用子流程模块的知识。您可以用两种不同的方式打开一个进程,您知道如何等待返回代码,并且知道如何与您创建的子进程通信。

在下一章中,我们将看到 sys 模块。