如何正确链接Python Popen调用?

时间:2018-03-07 21:48:40

标签: python subprocess popen

事实证明这并不像我想象的那么简单。我想要做的是通过将Popen命令设置为上一个命令的stdin(或stdout为第一个命令,使用PIPE链接一些命令{1}})。另外,我将命令的stdin设置为stderr - ed files。

我所看到的是,在open第一个命令的close之前,基本上这根本不起作用。当我写入第一个命令stdin时,我期待进程在管道链中进行通信,但在关闭第一个命令的stdin之前,没有任何内容写入第二个命令。同样,在关闭第一个命令的stdin之前,没有stderr个文件实际写入。

请考虑以下示例:

stdin

./cat.py

#!/usr/bin/python3 import sys, datetime def log(msg, file): file.write(str(datetime.datetime.now()) + ': ' + msg + '\n') log('starting now', sys.stdout) log('rofl', sys.stderr) for input_line in iter(sys.stdin.readline, ''): input_line = input_line.rstrip('\n') log(input_line, sys.stdout) log('lol ' + input_line, sys.stderr) log('ending now', sys.stdout)

./basic.py

这将启动一个4进程#!/usr/bin/python3 import sys, subprocess subproc = [] length = 4 for i in range(0, length): if i == 0: subproc.append(subprocess.Popen('./cat.py', bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=open(str(i) + '.stderr', 'w'))) elif i == length - 1: subproc.append(subprocess.Popen('./cat.py', bufsize=0, stdin=subproc[-1].stdout, stdout=open(str(i) + '.stdout', 'w'), stderr=open(str(i) + '.stderr', 'w'))) else: subproc.append(subprocess.Popen('./cat.py', bufsize=0, stdin=subproc[-1].stdout, stdout=subprocess.PIPE, stderr=open(str(i) + '.stderr', 'w'))) for input_line in iter(sys.stdin.readline, ''): input_line = input_line.rstrip('\n') subproc[0].stdin.write((input_line + '\n').encode()) 链并将Popen写入stderr0.stderr等,并将最终1.stderr写入stdout。输出行带有时间戳,因此您可以轻松验证特定子进程何时收到输入。从基本测试中考虑这些输出,我在输入之间等待了几秒钟。

3.stdout

0.stderr

2018-03-07 16:18:48.316847: rofl 2018-03-07 16:18:51.815376: lol hello 1 2018-03-07 16:18:55.561168: lol hello 2 2018-03-07 16:18:59.204273: lol hello 3

1.stderr

2018-03-07 16:18:48.320991: rofl 2018-03-07 16:19:01.888496: lol 2018-03-07 16:18:48.316825: starting now 2018-03-07 16:19:01.888508: lol 2018-03-07 16:18:51.815345: hello 1 2018-03-07 16:19:01.888515: lol 2018-03-07 16:18:55.561136: hello 2 2018-03-07 16:19:01.888523: lol 2018-03-07 16:18:59.204241: hello 3 2018-03-07 16:19:01.888531: lol 2018-03-07 16:19:01.886950: ending now

3.stdout

正如您所看到的,第一个子进程实时获取2018-03-07 16:18:48.323203: starting now 2018-03-07 16:19:01.899180: 2018-03-07 16:18:48.323766: starting now 2018-03-07 16:19:01.899208: 2018-03-07 16:19:01.895778: 2018-03-07 16:18:48.320973: starting now 2018-03-07 16:19:01.899216: 2018-03-07 16:19:01.895804: 2018-03-07 16:19:01.888476: 2018-03-07 16:18:48.316825: starting now 2018-03-07 16:19:01.899224: 2018-03-07 16:19:01.895813: 2018-03-07 16:19:01.888504: 2018-03-07 16:18:51.815345: hello 1 2018-03-07 16:19:01.899233: 2018-03-07 16:19:01.895820: 2018-03-07 16:19:01.888512: 2018-03-07 16:18:55.561136: hello 2 2018-03-07 16:19:01.899240: 2018-03-07 16:19:01.895829: 2018-03-07 16:19:01.888519: 2018-03-07 16:18:59.204241: hello 3 2018-03-07 16:19:01.899247: 2018-03-07 16:19:01.895836: 2018-03-07 16:19:01.888527: 2018-03-07 16:19:01.886950: ending now 2018-03-07 16:19:01.899254: 2018-03-07 16:19:01.895844: 2018-03-07 16:19:01.892480: ending now 2018-03-07 16:19:01.899262: 2018-03-07 16:19:01.895860: ending now 2018-03-07 16:19:01.899277: ending now ,然后链停止,直到我关闭第一个子进程的write。尽管我使用stdin进行所有bufsize=0次调用,但仍然如此。 (不使用Popen,在关闭0之前,甚至第一个子流程都没有收到任何write。)

我正在尝试做什么?这样做的正确方法是什么?

stdin在我的情况下不起作用,因为该函数会关闭communicate。我需要保持stdin打开整个过程来处理其他传入数据。我需要保持第一个stdin打开以接收额外的输入,同时命令的输出沿管道链发送,由下一个命令处理并在管道链中进一步发送。

有什么建议吗?

我使用的是Python 3.4.3。

谢谢,

2 个答案:

答案 0 :(得分:0)

在进程之间传输数据非常昂贵(相对于进程中的许多简单计算)。内核通过等待“大量”数据待处理 - 或直到管道关闭来分摊成本,因为显然没有必要等待更多数据。这发生在每个进程对之间。您的bufsize=0仅影响在父进程中写入,这就是为什么它仅用于为管道的第一阶段提供服务。

有些程序提供控制输出缓冲的选项(,它们可以要求内核尽早传输数据):Python本身提供python -u。 (如果它是您的 Python程序,请使用flush(),例如每个进程中建议的gddc。)否则,有一个(大部分)通用实用程序unbuffer这可能会让你立即做出回应。

答案 1 :(得分:0)

这是一个适合您的解决方案:

#!/usr/bin/python3
import sys, subprocess

length = 4
stdin = sys.stdin
stdout = subprocess.PIPE
command = ['python3', './cat.py']

for i in range(length):
    if i == length - 1:
        stdout = open('stdout', 'w')
    stderr = open('{}.stderr'.format(i), 'w')
    process = subprocess.Popen(command, stdin=stdin, stdout=stdout, stderr=stderr)
    stdin = process.stdout

process.communicate()

注释

  • 我在Linux上运行并且只运行'./cat.py'对我不起作用,我必须使用['python3', './cat.py']
  • 我的方法是通过链接一个过程来实现的。 stdout到下一个stdin
  • stdoutsubprocess.PIPE,除了最后一个,即文件
  • 在最后一个流程上调用communicate()将设置管道链

祝你有个美好的一天。