在python中复制Bash-tee命令行为



我有一组Bash脚本,我想在python中重新创建它们。这些脚本的一个关键特性是当我执行它们时它将把终端的内容保存到日志文件中。在Bash中,我简单地使用了tee命令。

2>&1 | tee "logfile.txt";

问题是为python找到平等的解决方案。

到目前为止,我发现了这个"谜题"的一半(解决方案A和B(,其中一个预期行为在其中一个脚本中有效,但在另一个中无效,反之亦然。

解决方案A(

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

with Popen(['ffmpeg','-i','1.webm','-y','1.mp3'], stdout=PIPE, stderr=STDOUT, bufsize=1) as p, 
open('logfile.txt', 'ab') as file:
for line in p.stdout:
sys.stdout.buffer.write(line)
file.write(line)

解决方案B(

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

with Popen(['ffmpeg','-i','1.webm','-y','1.mp3'], stdout=PIPE, bufsize=1, universal_newlines=True) as p:
logfile = open('logfile.txt', 'w')
for line in p.stdout:
print(line, end='')

我试图"合并"这两个代码片段的功能,但我不知道如何将其组合在一起。

我要查找的是python脚本文件中tee命令的EXACT行为复制。这意味着。。。

  • 终端的内容显示在终端窗口中AND保存到日志文件中(就像解决方案a一样(

  • 当我启动python脚本文件时,我想在终端中跟踪进程的进度,以检查离完成还有多远(就像解决方案B一样(。我不想盯着一个空白的屏幕,直到过程完成(解决方案a(。

我非常感谢您的帮助。

为了进行测试,我使用了一个webm格式的文件(通过youtubedl下载(,并在cygwin中使用ffmpeg将其转换为mp3。如果您想试用ffmpeg二进制文件,可以从这里下载https://www.ffmpeg.org/download.html

谢谢!

我做了一些测试,不,sys.stdout.flush((不能解决它。问题似乎在Popen/PPIPE实现本身——它在子进程和进程之间设置管道的方式引入了缓冲。

似乎能解决这个问题的是:

$ export PYTHONUNBUFFERED=1

在运行Python脚本的环境中。(变量可以设置为任何值。(

要在Python脚本中解决这个问题,可能有一种更优雅的方法,但这种相当奇怪的方法似乎对我有效

import os
from subprocess import run
if not "PYTHONUNBUFFERED" in os.environ:
os.environ["PYTHONUNBUFFERED"] = "1"
completed = run(sys.argv)
sys.exit(completed.returncode)

在问题230751中找到了一些指向此解决方案的指针。

您正在逐行读取,但ffmpeg不会输出不同的行。

您应该像tee那样,逐个缓冲区读取缓冲区,忽略换行符:

#!/usr/bin/env python3.8
import sys
from subprocess import Popen, PIPE, STDOUT
with Popen(['ffmpeg','-i','1.webm','-y','1.mp3'], stdout=PIPE, stderr=STDOUT, bufsize=0) as p, 
open('logfile.txt', 'ab') as file:
while buf := p.stdout.read(4096):
sys.stdout.buffer.write(buf);
sys.stdout.buffer.flush()
file.write(buf)

我决定重新打开这个案例,经过一些修补,我终于找到了解决方案,但我感谢大家的努力!

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

with Popen(['ffmpeg','-i','test.wav','-y','1.mp3'], stdout=PIPE, stderr=STDOUT, universal_newlines=True) as process, 
open('logfile.txt', 'w') as logfile:
for line in process.stdout:

print(line) 
logfile.write(line)
logfile.close()    

最新更新