2013-03-11 58 views
4

在bash,你可以一个过程的输出很容易地重定向到一个临时文件的描述符,它是所有的bash这样的自动处理的:击风格处理替代

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

或像这样:

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

看看它是如何不标准输入重定向:

$ 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是如何自动创建一个文件描述符,并允许您将程序的输出传递给另一个程序。

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

我一直在使用kmers的正常文件,只是读它,但我的程序现在基于用户参数在运行时生成一个特定的kmers列表。我想避免手动写入临时文件,因为处理文件权限可能会对我的原始用户造成问题。

这里是我的代码运行了一个程序,用一个实际的文件捕获标准输出“kmer_file”

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

我创建了一个名为generate_kmers函数返回一个可以很容易地写出到一个文件中的字符串(包括换行符)或一个StringIO。我也有一个Python脚本,是独立做同样的事情

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

这不起作用:

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选项并使用<()的实际双击,但我没有想到这一点。

谢谢!

+0

相关:[在子进程多个管道(http://stackoverflow.com/q/28840575/4279) – jfs 2015-09-22 10:51:15

回答

8

如果pram_axdnull理解"-"约定的意思: “从标准输入读取”,那么你可以:

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没有按:

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

如果由外部过程中产生的输入't了解"-"约定:

from subprocess import check_call 

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

为了避免等待进行,即生成的所有k链节,写/同时读取k链节,你可以使用在Unix os.mkfifo()(由@cdarke建议):

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) 

要使用外部程序生成的临时文件:

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() 
+0

如果pram_axdnull不理解“ - ”,有什么我可以做的吗? – MutantTurkey 2013-03-11 16:41:40

+0

@MutantTurkey:我已经更新了答案。 – jfs 2013-03-11 16:44:33

+0

或命名管道? 'os.mkfifo()'? – cdarke 2013-03-11 17:31:57

1

将一个类似文件的对象提供给subprocess.popen构造函数。

更多信息here

而且StringIO从琴弦类似文件的对象。

+0

大概还需要标准输入= PIPE为它工作也是如此。在bash中是不是像“blah <(echo foo)”等同于“echo foo | blah”? – silverjam 2013-03-11 16:29:50

+2

不,不是。它与stdin相似,但不是管道。常见的误解。它创建一个程序可以读取的附加文件描述符。看到这个例子: vim <(echo“Hello World”) – MutantTurkey 2013-03-11 16:31:24

+3

此外,StringIO不适用于管道到标准输入。由于StringIO仅在内存中定义,因此它没有文件描述符传递给子进程。 – 2013-03-11 16:50:00