如何连接Popen stdin的多个文件

Ton*_*roy 9 python pipe concatenation popen

我正在将一个bash脚本移植到python 2.6,并想要替换一些代码:

cat $( ls -tr xyz_`date +%F`_*.log ) | filter args > bzip2
Run Code Online (Sandbox Code Playgroud)

我想我想要一些类似于http://docs.python.org/release/2.6/library/subprocess.html上的"替换shell管道"示例,ala ...

p1 = Popen(["filter", "args"], stdin=*?WHAT?*, stdout=PIPE)
p2 = Popen(["bzip2"], stdin=p1.stdout, stdout=PIPE)
output = p2.communicate()[0]
Run Code Online (Sandbox Code Playgroud)

但是,我不确定如何最好地提供它p1stdin值,以便它连接输入文件.似乎我可以添加......

p0 = Popen(["cat", "file1", "file2"...], stdout=PIPE)
p1 = ... stdin=p0.stdout ...
Run Code Online (Sandbox Code Playgroud)

...但这似乎超越了使用(缓慢,低效)管道来调用具有重要功能的外部程序.(任何体面的shell都会在cat内部执行.)

所以,我可以设想一个满足文件对象API要求的自定义类,因此可以用于p1的stdin,连接任意其他文件对象.(编辑:现有答案解释了为什么这是不可能的)

难道蟒蛇2.6有一个机制,解决这方面的需要/想,或其他可能Popencat在Python界被认为是完美的罚款?

谢谢.

Ros*_*ron 5

您可以使用Python代码替换您正在执行的所有操作,但外部实用程序除外.这样,只要您的外部工具可移植,您的程序将保持可移植性.您还可以考虑将C++程序转换为库并使用Cython与其进行交互.正如Messa所示,date替换为time.strftime,使用globbing完成glob.globcat可以替换为读取列表中的所有文件并将其写入程序的输入.bzip2可以用bz2模块替换调用,但这会使程序复杂化,因为您必须同时读写.要做到这一点,你需要使用p.communicate或者一个线程,如果数据是巨大的(select.select将是一个更好的选择,但它不适用于Windows).

import sys
import bz2
import glob
import time
import threading
import subprocess

output_filename = '../whatever.bz2'
input_filenames = glob.glob(time.strftime("xyz_%F_*.log"))
p = subprocess.Popen(['filter', 'args'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
output = open(output_filename, 'wb')
output_compressor = bz2.BZ2Compressor()

def data_reader():
    for filename in input_filenames:
        f = open(filename, 'rb')
        p.stdin.writelines(iter(lambda: f.read(8192), ''))
    p.stdin.close()

input_thread = threading.Thread(target=data_reader)
input_thread.start()

with output:
    for chunk in iter(lambda: p.stdout.read(8192), ''):
        output.write(output_compressor.compress(chunk))

    output.write(output_compressor.flush())

input_thread.join()
p.wait()
Run Code Online (Sandbox Code Playgroud)

增加:如何检测文件输入类型

您可以使用文件扩展名或libmagic的Python绑定来检测文件的压缩方式.这是一个代码示例,它同时执行这两个操作,并自动选择magic是否可用.您可以选择适合您需求的部分并根据您的需求进行调整.本open_autodecompress应检测的MIME编码,并打开该文件与适当的解压缩(如果可用).

import os
import gzip
import bz2
try:
    import magic
except ImportError:
    has_magic = False
else:
    has_magic = True


mime_openers = {
    'application/x-bzip2': bz2.BZ2File,
    'application/x-gzip': gzip.GzipFile,
}

ext_openers = {
    '.bz2': bz2.BZ2File,
    '.gz': gzip.GzipFile,
}


def open_autodecompress(filename, mode='r'):
    if has_magic:
        ms = magic.open(magic.MAGIC_MIME_TYPE)
        ms.load()
        mimetype = ms.file(filename)
        opener = mime_openers.get(mimetype, open)
    else:
        basepart, ext = os.path.splitext(filename)
        opener = ext_openers.get(ext, open)
    return opener(filename, mode)
Run Code Online (Sandbox Code Playgroud)