如何通过Pythons子进程管道到文件或shell程序?

时间:2016-07-01 13:08:44

标签: python bash shell subprocess popen

我正在使用一些相当大的gzip压缩文本文件,我必须解压缩,编辑和重新压缩。我使用Pythons gzip模块进行解压缩和压缩,但我发现我当前的实现远非最佳:

input_file = gzip.open(input_file_name, 'rb')

output_file = gzip.open(output_file_name, 'wb')

for line in input_file:
    # Edit line and write to output_file

这种方法难以忍受 - 可能是因为使用gzip模块进行每行迭代涉及巨大的开销:我最初还运行一个行计数例程,其中我 - 使用gzip模块 - 读取文件的块和然后计算每个块中换行符的数量,这非常快!

因此,其中一个优化应该是以块的形式读取我的文件,然后只有在解压缩块后才进行每行迭代。

作为一个额外的优化,我已经看到了一些通过子进程解压缩shell命令的建议。使用这种方法,上面第一行的等价物可以是:

from subprocess import Popen, PIPE

file_input = Popen(["zcat", fastq_filename], stdout=PIPE)

input_file = file_input.stdout

使用此方法,input_file将成为类文件对象。我不确切地知道它在可用属性和方法方面与真实文件对象有何不同,但一个区别是你显然不能使用seek,因为它是一个流而不是一个文件。

这确实运行得更快,它应该 - 除非你在单个核心机器中运行你的脚本声明。后者必须意味着如果可能的话,子进程会自动将不同的线程传送到不同的核心,但我不是那里的专家。

现在问到我目前的问题:我想以类似的方式压缩输出。也就是说,我不想使用Pythons gzip模块,而是将它传递给子进程,然后调用shell gzip。通过这种方式,我可以在单独的核心中进行阅读,编辑和书写,这对我来说听起来非常有效。 我对此做了一个微不足道的尝试,但尝试写入output_file导致一个空文件。最初,我使用touch命令创建一个空文件,因为如果文件不存在,Popen会失败:

call('touch ' + output_file_name, shell=True)

output = Popen(["gzip", output_file_name], stdin=PIPE)

output_file = output.stdin

非常感谢任何帮助,我正在使用Python 2.7。感谢。

2 个答案:

答案 0 :(得分:1)

你的意思是output_file = gzip_process.stdin。之后,您可以使用output_file,因为您之前使用过gzip.open()个对象(不寻求)。

如果结果文件为空,请检查您在Python脚本末尾调用output_file.close()gzip_process.wait()。此外,gzip的使用可能不正确:如果gzip将压缩输出写入其标准输出,则将stdout=gzip_output_file传递给gzip_output_file = open(output_file_name, 'wb', 0)

答案 1 :(得分:1)

这是一个如何做到这一点的工作示例:

#!/usr/bin/env python

from subprocess import Popen, PIPE

output = ['this', 'is', 'a', 'test']

output_file_name = 'pipe_out_test.txt.gz'

gzip_output_file = open(output_file_name, 'wb', 0)

output_stream = Popen(["gzip"], stdin=PIPE, stdout=gzip_output_file)  # If gzip is supported

for line in output:
    output_stream.stdin.write(line + '\n')

output_stream.stdin.close()
output_stream.wait()

gzip_output_file.close()

如果我们的脚本只写入控制台并且我们希望输出压缩,那么上面的shell命令可能是:

script_that_writes_to_console | gzip > output.txt.gz