将数据流化到命令中子流程.Popen

2024-05-23 23:17:17 发布

您现在位置:Python中文网/ 问答频道 /正文

我经常需要对包含标题的文件集合进行排序。因为排序依赖于标题的内容,这个用例比类似的问题(例如,Is there a way to ignore header lines in a UNIX sort?)更复杂。在

我希望使用Python来读取文件,输出第一个文件的头,然后将尾部导入sort。我试过这个作为概念的证明:

#!/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包含以下行

^{pr2}$

我得到:

# 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

问题是communication需要一个字符串,而管道在写入后关闭。这意味着内容必须完全读入内存。交流不需要发电机(我试过了)。在

更简单的例子是:

>>> 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或其他方法)?在


Tags: 文件inimportusrstdinlinesortfile
3条回答

直接写到管道上:

#!/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()

您可以使用stdinstdout的写入/读取,但是根据您的子流程,您需要子流程的“刷新机制”来处理您的输入。下面的代码适用于第一部分,但由于它关闭了stdin,因此也会终止子进程。如果使用flush()更改它,或者可以添加一些尾随字符来推送子进程,则可以使用它。否则,我建议您看看Multithreading in Python,尤其是{}。在

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'

对于您的特定情况,如果您只为单个标准句柄传递了subprocess.PIPE(在您的例子中,stdin),那么在您的示例中,您可以安全地反复调用sorter.stdin.write(line)。完成输出写入后,调用sorter.stdin.close()以便sort知道输入已完成,并且它可以执行实际的排序和输出工作(sorter.communicate()可能也可以工作;否则,在关闭stdin之后,您可能需要调用sorter.wait()让它完成)。在

如果需要处理多个管道标准句柄,正确的方法是使用^{}为每个必须在第一个之外处理的管道指定一个线程(概念上相对简单,但是很重,并且引入了线程的所有麻烦),或者使用^{}模块(或者在python3.4+中,^{}模块),这是相当棘手的,但可以(在某些情况下)更有效。最后,还有一个creating temporary files for output,因此可以在进程写入文件时直接写入进程的{};然后可以在空闲时读取文件(注意,在退出之前,子进程不必刷新自己的输出缓冲区,因此输出可能不会立即响应您的输入到达进一步的输入和输出已经填充并刷新了缓冲区)。在

subprocess.Popen.communicate()方法使用线程或select模块原语本身(取决于操作系统支持;实现在various ^{} methods here下)每当您为一个以上的标准句柄传递subprocess.PIPE;这就是您必须这样做的方式。在

相关问题 更多 >