2013-03-11 60 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