使用Python的Popen替换Bash样式进程

时间:2013-03-11 16:17:51

标签: python bash subprocess popen

在Bash中,您可以轻松地将进程的输出重定向到临时文件描述符,并且它由bash自动处理,如下所示:

$ mydaemon --config-file <(echo "autostart: True \n daemonize: True")

或者像这样:

$ wc -l <(ls)
15 /dev/fd/63

看看它是如何不是stdin重定向的:

$ vim <(echo "Hello World") 
vim opens a text file containing "Hello world"
$ echo  "Hello World" | vim
Vim: Warning: Input is not from a terminal

您可以在第二个示例中看到bash如何自动创建文件描述符,并允许您将程序的输出传递给另一个程序。

现在回答我的问题:如何在子进程模块中使用Popen对Python做同样的事情?

我一直在使用普通的kmers文件并只是读取它,但我的程序现在根据用户参数在运行时生成一个特定的kmers列表。我想避免手动写入临时文件,因为处理文件权限可能会给我的原始用户带来问题。

这是我运行程序并使用实际文件“kmer_file”捕获stdout的代码

input_file = Popen(["pram_axdnull", str(kmer), input_file, kmer_file], stdout=PIPE)

我创建了一个名为generate_kmers的函数,它返回一个可以轻松写入文件(包括换行符)或StringIO的字符串。我还有一个独立的python脚本来做同样的事情

所以现在我想把它作为我的第三个参数传递:

这不起作用:

kmer_file = stringIO(generate_kmers(3))
input_file = Popen(["pram_axdnull", str(kmer), input_file, kmer_file], stdout=PIPE)

这也不是:

kmer_file = Popen(["generate_kmers", str(kmer)], stdout=PIPE)
input_file = Popen(["pram_axdnull", str(kmer), input_file, kmer_file.stdout], stdout=PIPE)

所以我没有想法。

有谁知道解决此问题的好方法?我在考虑使用shell = True选项并使用&lt;()的实际基础,但我还没想到。

谢谢!

2 个答案:

答案 0 :(得分:10)

如果pram_axdnull"-"约定理解为:“从stdin读取”那么您可以:

p = Popen(["pram_axdnull", str(kmer), input_filename, "-"],
          stdin=PIPE, stdout=PIPE)
output = p.communicate(generate_kmers(3))[0]

如果输入是由外部进程生成的:

kmer_proc = Popen(["generate_kmers", str(kmer)], stdout=PIPE)
p = Popen(["pram_axdnull", str(kmer), input_filename, "-"],
          stdin=kmer_proc.stdout, stdout=PIPE)
kmer_proc.stdout.close()
output = p.communicate()[0]

如果pram_axdnull 了解"-"惯例:

import os
import tempfile
from subprocess import check_output

with tempfile.NamedTemporaryFile() as file:
    file.write(generate_kmers(3))
    file.delete = False

try:
    p = Popen(["pram_axdnull", str(kmer), input_filename, file.name],
              stdout=PIPE)
    output = p.communicate()[0]
    # or
    # output = check_output(["pram_axdnull", str(kmer), input_filename, 
                             file.name])
finally:
    os.remove(file.name)

使用外部流程生成临时文件:

from subprocess import check_call

with tempfile.NamedTemporaryFile() as file:
    check_call(["generate_kmers", str(kmer)], stdout=file)
    file.delete = False

为了避免等待所有kmers被生成,即同时写入/读取kmers,你可以在Unix上使用os.mkfifo()(由@cdarke建议):

import os
import shutil
import tempfile
from contextlib import contextmanager
from subprocess import Popen, PIPE

@contextmanager
def named_pipe():
    dirname = tempfile.mkdtemp()
    try:
        path = os.path.join(dirname, 'named_pipe')
        os.mkfifo(path)
        yield path
    finally:
        shutil.rmtree(dirname)

with named_pipe() as path:
    p = Popen(["pram_axdnull", str(kmer), input_filename, path],
              stdout=PIPE) # read from path
    with open(path, 'wb') as wpipe:
        kmer_proc = Popen(["generate_kmers", str(kmer)],
                          stdout=wpipe) # write to path
    output = p.communicate()[0]
    kmer_proc.wait()

答案 1 :(得分:1)

将类文件对象提供给stdin到subprocess.popen构造函数。

更多信息here

StringIO从字符串中获取类似文件的对象。