2012-01-02 185 views
3

此Python代碼通過Perl腳本管理數據正常。如何使用Python將標準輸入/標準輸出傳遞給Perl腳本

import subprocess 
kw = {} 
kw['executable'] = None 
kw['shell'] = True 
kw['stdin'] = None 
kw['stdout'] = subprocess.PIPE 
kw['stderr'] = subprocess.PIPE 
args = ' '.join(['/usr/bin/perl','-w','/path/script.perl','<','/path/mydata']) 
subproc = subprocess.Popen(args,**kw) 
for line in iter(subproc.stdout.readline, ''): 
    print line.rstrip().decode('UTF-8') 

但是,它要求我首先將我的緩衝區保存到磁盤文件(/ path/mydata)。它是清潔器遍歷在Python代碼中的數據,並通過線由行到這樣的子過程:

import subprocess 
kw = {} 
kw['executable'] = '/usr/bin/perl' 
kw['shell'] = False 
kw['stderr'] = subprocess.PIPE 
kw['stdin'] = subprocess.PIPE 
kw['stdout'] = subprocess.PIPE 
args = ['-w','/path/script.perl',] 
subproc = subprocess.Popen(args,**kw) 
f = codecs.open('/path/mydata','r','UTF-8') 
for line in f: 
    subproc.stdin.write('%s\n'%(line.strip().encode('UTF-8'))) 
    print line.strip() ### code hangs after printing this ### 
    for line in iter(subproc.stdout.readline, ''): 
     print line.rstrip().decode('UTF-8') 
subproc.terminate() 
f.close() 

代碼發送所述第一線到子後使用readline掛起。我有其他可執行文件完全使用完全相同的代碼。

我的數據文件可能非常大(1.5 GB)有沒有辦法在不保存到文件的情況下管道化數據?我不想重寫perl腳本以與其他系統兼容。

回答

1

謝謝srgerg。我也嘗試了線程解決方案。然而,這種解決方案一直懸而未決。我以前的代碼和srgerg的代碼都缺少最終解決方案,您的提示給了我最後一個想法。

最終解決方案寫入足夠的虛擬數據強制緩衝區中的最終有效行。爲了支持這一點,我添加了跟蹤向stdin寫入了多少有效行的代碼。線程循環打開輸出文件,保存數據,並在讀取行等於有效輸入行時斷開。這個解決方案確保它可以爲任何大小的文件逐行讀寫。

def std_output(stdout,outfile=''): 
    out = 0 
    f = codecs.open(outfile,'w','UTF-8') 
    for line in iter(stdout.readline, ''): 
     f.write('%s\n'%(line.rstrip().decode('UTF-8'))) 
     out += 1 
     if i == out: break 
    stdout.close() 
    f.close() 

outfile = '/path/myout' 
infile = '/path/mydata' 

subproc = subprocess.Popen(args,**kw) 
t = threading.Thread(target=std_output,args=[subproc.stdout,outfile]) 
t.daemon = True 
t.start() 

i = 0 
f = codecs.open(infile,'r','UTF-8') 
for line in f: 
    subproc.stdin.write('%s\n'%(line.strip().encode('UTF-8'))) 
    i += 1 
subproc.stdin.write('%s\n'%(' '*4096)) ### push dummy data ### 
f.close() 
t.join() 
subproc.terminate() 
0

參見有關使用Popen.stdinPopen.stdout(剛好高於Popen.stdin)的手冊中的警告:

警告:使用communicate()而非.stdin.write.stdout.read.stderr.read到避免由於任何其他操作系統管道緩衝區填滿和阻塞兒童造成的死鎖流程。

我認識到,在內存中一個千兆字節和半弦一下子還不是很理想的,但使用communicate()工作,而你所觀察到,一旦方法OS管道緩衝區填滿,stdin.write() + stdout.read()方式可能會死鎖。

是否使用communicate()對您可行?

1

你的代碼是阻止在該行:

for line in iter(subproc.stdout.readline, ''): 

,因爲這個迭代可以終止的唯一方法是當達到EOF(結束文件),當子終止這會發生。您不希望等到進程終止,然而,您只想等待完成處理髮送給它的行。

此外,你正遇到緩衝問題,正如克里斯摩根已經指出的那樣。另一個question on stackoverflow討論瞭如何使用子流程執行非阻塞讀取。我砍死了代碼的快速和骯髒的適應從這個問題您的問題:

def enqueue_output(out, queue): 
    for line in iter(out.readline, ''): 
     queue.put(line) 
    out.close() 

kw = {} 
kw['executable'] = '/usr/bin/perl' 
kw['shell'] = False 
kw['stderr'] = subprocess.PIPE 
kw['stdin'] = subprocess.PIPE 
kw['stdout'] = subprocess.PIPE 
args = ['-w','/path/script.perl',] 
subproc = subprocess.Popen(args, **kw) 
f = codecs.open('/path/mydata','r','UTF-8') 
q = Queue.Queue() 
t = threading.Thread(target = enqueue_output, args = (subproc.stdout, q)) 
t.daemon = True 
t.start() 
for line in f: 
    subproc.stdin.write('%s\n'%(line.strip().encode('UTF-8'))) 
    print "Sent:", line.strip() ### code hangs after printing this ### 
    try: 
     line = q.get_nowait() 
    except Queue.Empty: 
     pass 
    else: 
     print "Received:", line.rstrip().decode('UTF-8') 

subproc.terminate() 
f.close() 

這很可能是你需要修改這個代碼,但至少它不會阻止。

+0

謝謝。我用最終的代碼添加了一個答案 – tahoar 2012-01-02 13:57:11

相關問題