2013-03-20 90 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

相关问题