2013-03-20 269 views
24

我正在使用Popen調用一個不斷將其stdout和stderr寫入日誌文件的shell腳本。有什麼辦法可以連續(到屏幕上)同時輸出日誌文件,或者讓shell腳本同時寫入日誌文件和stdout?Python Popen:同時寫入標準輸出和日誌文件

我基本上想要做這樣的事情在Python:

cat file 2>&1 | tee -a logfile #"cat file" will be replaced with some script 

同樣,這種管標準錯誤/標準輸出一起三通,它都寫入到標準輸出和我的日誌文件。

我知道如何將stdout和stderr寫入Python中的日誌文件。當我被困在是如何把這些複製到屏幕:

subprocess.Popen("cat file", shell=True, stdout=logfile, stderr=logfile) 

當然我可以做這樣的事情,但有什麼辦法做到這一點沒有三通和外殼文件描述符重定向?:

subprocess.Popen("cat file 2>&1 | tee -a logfile", shell=True) 
+0

相關:[?Python的子進程讓孩子的輸出到文件和終端(http://stackoverflow.com/q/4984428/4279) – jfs 2013-09-23 17:12:49

回答

25

您可以使用一個管道,從程序的stdout讀取數據並將其寫入到所有你想要的地方:

import sys 
import subprocess 

logfile = open('logfile', 'w') 
proc=subprocess.Popen(['cat', 'file'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 
for line in proc.stdout: 
    sys.stdout.write(line) 
    logfile.write(line) 
proc.wait() 

UPDATE

在Python 3中,universal_newlines參數控制着如何使用管道。如果False,管道讀取返回bytes對象,並可能需要解碼(例如,line.decode('utf-8'))以獲取字符串。如果True,蟒蛇爲你做

Changed in version 3.3: When universal_newlines is True, the class uses the encoding locale.getpreferredencoding(False) instead of locale.getpreferredencoding(). See the io.TextIOWrapper class for more information on this change.

+5

您還可以創建一個文件就像封裝了這個功能的對象,然後在調用「Popen」時使用它來代替'stdout' /'stderr'。 – 2013-03-20 21:47:48

+1

@ sr2222 - 我也喜歡這個想法......除了現在我想到它...,它們是操作系統管道,而不是python對象,所以甚至工作? – tdelaney 2013-03-20 21:48:45

+0

爲什麼你調用proc.wait()後有引用proc.stdout?我有點困惑在哪裏使用proc.wait()。 – imagineerThat 2013-03-20 21:55:59

5

解碼爲模擬:subprocess.call("command 2>&1 | tee -a logfile", shell=True),而不必調用tee命令:

#!/usr/bin/env python2 
from subprocess import Popen, PIPE, STDOUT 

p = Popen("command", stdout=PIPE, stderr=STDOUT, bufsize=1) 
with p.stdout, open('logfile', 'ab') as file: 
    for line in iter(p.stdout.readline, b''): 
     print line, #NOTE: the comma prevents duplicate newlines (softspace hack) 
     file.write(line) 
p.wait() 

要解決可能的緩衝問題(如果輸出延遲),請參閱Python: read streaming input from subprocess.communicate()鏈接。

這裏是Python 3的版本:

#!/usr/bin/env python3 
import sys 
from subprocess import Popen, PIPE, STDOUT 

with Popen("command", stdout=PIPE, stderr=STDOUT, bufsize=1) as p, \ 
    open('logfile', 'ab') as file: 
    for line in p.stdout: # b'\n'-separated lines 
     sys.stdout.buffer.write(line) # pass bytes as is 
     file.write(line) 
+1

你應該提到你可以在完成後在p.returncode中找到返回代碼。 – kdubs 2016-03-16 22:23:04

+0

@kdubs:這與問題無關。你爲什麼認爲我*「應該提到」*它? – jfs 2016-03-16 22:47:26

+3

雖然我同意他沒有要求,但似乎應該檢查退貨狀態。我希望能在這裏找到它。似乎會使答案完整。也許「應該」是強大的。 – kdubs 2016-03-17 10:46:31

相關問題