55

我正在使用 Popen 调用一个 shell 脚本,该脚本不断将其 stdout 和 stderr 写入日志文件。有没有办法同时连续输出日志文件(到屏幕上),或者让 shell 脚本同时写入日志文件和标准输出?

我基本上想在 Python 中做这样的事情:

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

同样,这将 stderr/stdout 一起传送到 tee,tee 将其同时写入 stdout 和我的日志文件。

我知道如何在 Python 中将 stdout 和 stderr 写入日志文件。我被困的地方是如何将这些复制回屏幕:

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

当然,我可以做这样的事情,但是有没有办法在没有 tee 和 shell 文件描述符重定向的情况下做到这一点?:

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

3 回答 3

53

您可以使用管道从程序的标准输出中读取数据并将其写入您想要的所有位置:

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

更新

在 python 3 中,universal_newlines参数控制管道的使用方式。如果False,管道读取返回bytes对象并且可能需要被解码(例如,line.decode('utf-8'))以获得字符串。如果True,python 会为你解码

在 3.3 版更改:当 universal_newlines 为 True 时,该类使用编码 locale.getpreferredencoding(False) 而不是 locale.getpreferredencoding()。有关此更改的更多信息,请参阅 io.TextIOWrapper 类。

于 2013-03-20T21:46:35.577 回答
18

模拟: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 中的链接:从 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)
于 2016-01-05T05:22:58.107 回答
3

为交互式应用程序逐字节写入终端

此方法立即将它获得的任何字节写入标准输出,这更接近于模拟 的行为tee,尤其是对于交互式应用程序。

主文件

#!/usr/bin/env python3
import os
import subprocess
import sys
with subprocess.Popen(sys.argv[1:], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) as proc, \
        open('logfile.txt', 'bw') as logfile:
    while True:
        byte = proc.stdout.read(1)
        if byte:
            sys.stdout.buffer.write(byte)
            sys.stdout.flush()
            logfile.write(byte)
            # logfile.flush()
        else:
            break
exit_status = proc.returncode

睡眠.py

#!/usr/bin/env python3
import sys
import time
for i in range(10):
    print(i)
    sys.stdout.flush()
    time.sleep(1)

首先,我们可以进行非交互式完整性检查:

./main.py ./sleep.py

我们看到它实时计入标准输出。

接下来,对于交互式测试,您可以运行:

./main.py bash

然后,您键入的字符会在您键入时立即出现在终端上,这对于交互式应用程序非常重要。这是运行时发生的情况:

bash | tee logfile.txt

此外,如果您希望输出立即显示在 ouptut 文件中,则还可以添加:

logfile.flush()

tee不这样做,我担心它会影响性能。您可以通过以下方式轻松测试:

tail -f logfile.txt

相关问题:子进程命令的实时输出

在 Ubuntu 18.04、Python 3.6.7 上测试。

于 2018-08-30T06:49:54.030 回答