我经常需要对包含标题的文件集合进行排序。因为排序取决于标题的内容,所以这个用例比类似的问题更复杂(例如,Is there a way to ignore header lines in a UNIX sort?)。
我希望使用Python来读取文件,输出第一个文件的标题,然后将尾部管道排序。我已经尝试过这个概念证明:
#!/usr/bin/env python
import io
import subprocess
import sys
header_printed = False
sorter = subprocess.Popen(['sort'], stdin=subprocess.PIPE)
for f in sys.argv[1:]:
fd = io.open(f,'r')
line = fd.readline()
if not header_printed:
print(line)
header_printed = True
sorter.communicate(line)
当调用header-sort fileA fileB
时,fileA和fileB包含
c float int
Y 0.557946 413
F 0.501935 852
F 0.768102 709
我明白了:
# sort file 1
Traceback (most recent call last):
File "./archive/bin/pipetest", line 17, in <module>
sorter.communicate(line)
File "/usr/lib/python2.7/subprocess.py", line 785, in communicate
self.stdin.write(input)
ValueError: I/O operation on closed file
问题是通信需要一个字符串,管道在写入后关闭。这意味着必须将内容完全读入内存。沟通不带发电机(我试过)。
更简单的证明是:
>>> import subprocess
>>> p = subprocess.Popen(['tr', 'a-z', 'A-Z'], stdin=subprocess.PIPE)
>>> p.communicate('hello')
HELLO(None, None)
>>> p.communicate('world')
Traceback (most recent call last):
File "<ipython-input-14-d6873fd0f66a>", line 1, in <module>
p.communicate('world')
File "/usr/lib/python2.7/subprocess.py", line 785, in communicate
self.stdin.write(input)
ValueError: I/O operation on closed file
所以,问题是,在Python中将数据流式传输到管道中的正确方法(使用Popen或其他方式)是什么?
答案 0 :(得分:2)
对于您的具体情况,如果您仅通过subprocess.PIPE
获取单个标准句柄(在您的情况下为stdin
),那么在您的示例中,您可以安全地调用sorter.stdin.write(line)
过度。完成输出后,调用sorter.stdin.close()
使sort
知道输入已完成,并且它可以执行实际的排序和输出工作(sorter.communicate()
没有参数可能也会起作用;否则,在关闭stdin
之后,你可能想要致电sorter.wait()
让它完成。)
如果你需要处理多个管道标准句柄,正确的方法是threading
,每个管道必须处理超出第一个管道的专用线程(概念相对简单,但重量级,并介绍所有头痛的线程),或使用select
模块(或在Python 3.4+,selectors
模块),这是非常棘手的,但可以(在某些情况下)更有效。最后,有creating temporary files for output,因此您可以在进程写入文件时直接写入进程的stdin
(因此不会阻塞);然后,您可以随意读取该文件(请注意,子进程在退出之前不一定会刷新它自己的输出缓冲区,因此输出可能无法及时响应您的输入,直到进一步的输入和输出已填满并刷新缓冲液)。
subprocess.Popen
.communicate()
方法使用线程或select
模块原语本身(取决于操作系统支持;实现在various _communicate
methods here下),只要您通过{{ 1}}用于多个标准句柄;这就是你必须要做的事情。
答案 1 :(得分:1)
直接写入管道:
#!/usr/bin/env python2
import fileinput
import subprocess
process = subprocess.Popen(['sort'], stdin=subprocess.PIPE)
with process.stdin as pipe, fileinput.FileInput() as file:
for line in file:
if file.isfirstline(): # print header
print line,
else: # pipe tails
pipe.write(line)
process.wait()
答案 2 :(得分:0)
您可以使用stdin
和stdout
的写入/读取,但是根据您的子流程,您需要一个“刷新机制”来让子流程处理您的输入。下面的代码适用于第一部分,但由于它关闭stdin
,它也会杀死子进程。如果您使用flush()
更改它,或者如果您可以添加一些尾随字符来推送您的子流程,那么您可以使用它。否则,我建议您查看Multithreading in Python,尤其是pipes
。
p=subprocess.Popen(['tr','a-z','A-Z'],stdin=subprocess.PIPE,stdout=subprocess.PIPE)
p.stdin.write("hello\n")
p.stdin.close()
p.stdout.readline()
'HELLO\n'