使用生成器作为子流程输入;出现“对关闭文件的I / O操作”异常

xyl*_*u00 2 python subprocess generator popen

我有一个很大的文件,需要馈入另一个命令才能处理。我可以将处理后的数据另存为临时文件,但要避免。我编写了一个生成器,该生成器一次处理每一行,然后按照脚本将输入作为外部输入。但是在循环的第二轮中,出现了“对关闭文件的I / O操作”异常:

cmd = ['intersectBed', '-a', 'stdin', '-b', bedfile]
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
for entry in my_entry_generator: # <- this is my generator
    output = p.communicate(input='\t'.join(entry) + '\n')[0]
    print output
Run Code Online (Sandbox Code Playgroud)

我读了另一个使用p.stdin.write的类似问题。但仍然有同样的问题。

我做错了什么?

[编辑]我将以下两个语句替换为以下内容(感谢SpliFF):

    output = p.communicate(input='\t'.join(entry) + '\n')
    if output[1]: print "error:", output[1]
    else: print output[0]
Run Code Online (Sandbox Code Playgroud)

看看外部程序是否有任何错误。但不是。p.communication线上仍然有相同的例外。

Tho*_*ers 6

对象的communicate方法subprocess.Popen只能调用一次。它的作用是读取所有stdout和stderr输出时,将您提供的输入发送给该进程。所谓“全部”,是指它等待进程退出,以便知道所有输出。一旦communicate返回,该过程将不再存在。

如果要使用communicate,则必须在循环中重新启动该过程,或者给它一个字符串,该字符串是生成器的所有输入。如果要进行流通信,一点一点发送数据,则不必使用communicate。相反,您需要p.stdin在从p.stdout和读取时进行写入p.stderr。这样做很棘手,因为您无法确定哪个输出是由哪个输入引起的,并且因为您很容易陷入死锁。有第三方库可以帮助您解决此问题,例如Twisted。

如果要交互地执行此操作,发送一些数据,然后等待并处理结果,然后再发送更多数据,则事情会变得更加困难。您可能应该使用类似的第三方库pexpect

当然,如果您可以仅在循环内开始该过程,那么这会容易得多:

cmd = ['intersectBed', '-a', 'stdin', '-b', bedfile]
for entry in my_entry_generator:
    p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    output = p.communicate(input='\t'.join(entry) + '\n')[0]
    print output
Run Code Online (Sandbox Code Playgroud)