如何通过 Python 子进程管道传输到文件或 shell 程序?
How do I pipe to a file or shell program via Pythons subprocess?
我正在处理一些相当大的 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,因为它是流而不是文件。
这确实 运行 更快而且它应该 - 除非你 运行 你的脚本在单核机器上声称是。后者必须意味着 subprocess 如果可能会自动将不同的线程发送到不同的内核,但我不是这方面的专家。
现在解决我当前的问题:我想以类似的方式压缩我的输出。也就是说,我不想使用 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。谢谢
你的意思是 output_file = gzip_process.stdin
。之后你可以使用 output_file
就像你之前使用过 gzip.open()
对象(不求)。
如果结果文件为空,请检查您是否在 Python 脚本末尾调用了 output_file.close()
和 gzip_process.wait()
。此外,gzip
的用法可能不正确:如果 gzip
将压缩输出写入其标准输出,则传递 stdout=gzip_output_file
where gzip_output_file = open(output_file_name, 'wb', 0)
.
这是一个如何做到这一点的工作示例:
#!/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
我正在处理一些相当大的 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,因为它是流而不是文件。
这确实 运行 更快而且它应该 - 除非你 运行 你的脚本在单核机器上声称是。后者必须意味着 subprocess 如果可能会自动将不同的线程发送到不同的内核,但我不是这方面的专家。
现在解决我当前的问题:我想以类似的方式压缩我的输出。也就是说,我不想使用 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。谢谢
你的意思是 output_file = gzip_process.stdin
。之后你可以使用 output_file
就像你之前使用过 gzip.open()
对象(不求)。
如果结果文件为空,请检查您是否在 Python 脚本末尾调用了 output_file.close()
和 gzip_process.wait()
。此外,gzip
的用法可能不正确:如果 gzip
将压缩输出写入其标准输出,则传递 stdout=gzip_output_file
where gzip_output_file = open(output_file_name, 'wb', 0)
.
这是一个如何做到这一点的工作示例:
#!/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