在Python中分叉多个shell命令/进程的最佳方法?

时间:2011-11-11 05:34:46

标签: python shell

我在os.fork和子进程/多处理模块中看到的大多数示例都展示了如何派生调用python脚本的新实例或一大块python代码。同时生成一组任意shell命令的最佳方法是什么?

我想,我可以使用subprocess.call或其中一个Popen命令并将输出传输到一个文件,我相信它会立即返回,至少是调用者。我知道这并不难,我只想弄清楚最简单,最恐怖的方式。

提前致谢

5 个答案:

答案 0 :(得分:3)

subprocess.Popen的所有来电都会立即返回给来电者。这是对waitcommunicate的调用阻止。因此,您需要做的就是使用subprocess.Popen(将stdin设置为/ dev / null以确保安全性)启动多个进程,然后逐个调用communicate直到它们全部完成。< / p>

当然,我假设你只是想尝试启动一堆不相关的(即没有管道连接)命令。

答案 1 :(得分:1)

  

我想,我可以只使用subprocess.call或其中一个Popen   命令并将输出传递给文件,我相信它会返回   立即,至少对来电者。

如果你想处理数据,这不是一个好方法。

在这种情况下,做得更好

sp = subprocess.Popen(['ls', '-l'], stdout=subprocess.PIPE)

然后sp.communicate()或直接从sp.stdout.read()阅读。

如果以后在调用程序中处理数据,有两种方法:

  1. 您可以尽快检索数据,可以通过一个单独的线程,阅读它们并将它们存储在消费者可以获得它们的地方。

  2. 您可以让生成子流程具有块,并在需要时从中检索数据。子进程产生的数据与管道缓冲区中的数据一样多(通常为64 kiB),然后阻止进一步写入。只要您需要数据,就可以从子流程对象read()(也可能是stdoutstderr并使用它们 - 或者再次使用{{1}在那个晚些时候。

  3. 如果生成数据需要很长时间,方法1就可以了,所以你的wprogram必须等待。

    如果数据的大小非常庞大和/或数据产生得如此之快以至于缓冲毫无意义,那么第2种方式将是首选。

答案 2 :(得分:1)

我喜欢使用PTY而不是管道。对于我只想捕获错误消息的一堆进程,我做了这个。

RNULL = open('/dev/null', 'r')
WNULL = open('/dev/null', 'w')
logfile = open("myprocess.log", "a", 1)
REALSTDERR = sys.stderr
sys.stderr = logfile

下一部分是一个产生约30个过程的循环。

sys.stderr = REALSTDERR
master, slave = pty.openpty()
self.subp = Popen(self.parsed, shell=False, stdin=RNULL, stdout=WNULL, stderr=slave)
sys.stderr = logfile

在此之后,我有一个select循环,它收集了任何错误消息并将它们发送到单个日志文件。使用PTY意味着我从不必担心部分线条混乱,因为线条规则提供了简单的框架。

答案 3 :(得分:1)

在所有可能的情况下都没有最好的方法。最好的取决于手头的问题。

以下是如何生成进程并将其输出保存到组合stdout / stderr的文件中:

import subprocess
import sys

def spawn(cmd, output_file):
    on_posix = 'posix' in sys.builtin_module_names
    return subprocess.Popen(cmd, close_fds=on_posix, bufsize=-1,
                            stdin=open(os.devnull,'rb'),
                            stdout=output_file,
                            stderr=subprocess.STDOUT)

生成可以与脚本并行运行的多个进程:

processes, files = [], []
try:
    for i, cmd in enumerate(commands):
        files.append(open('out%d' % i, 'wb'))
        processes.append(spawn(cmd, files[-1]))
finally:
    for p in processes:
        p.wait()
    for f in files: 
        f.close()

注意:cmd是一个无处不在的列表。

答案 4 :(得分:0)

请参阅an older answer of mine including code snippets

  • 使用进程而非阻塞I / O的线程,因为它们可以更可靠地进行p.terminated()
  • 实现可重新触发的超时监视程序,在发生某些输出时重新开始计数
  • 实施长期超时监视程序以限制整体运行时间
  • 可以输入标准输入(虽然我只需要输入一次短字符串)
  • 可以在通常的Popen中捕获stdout / stderr(仅对stdout进行编码,并将stderr重定向到stdout;但可以轻松分离)
  • 这几乎是实时的,因为它只检查输出每0.2秒。但你可以减少这个或轻松删除等待间隔
  • 许多调试打印输出仍然能够看到什么时候发生。

为了生成多个并发命令,您需要更改类RunCmd以实例化多个读取输出/写入输入队列并生成多个Popen子进程。