Репликация поведения команды Bash tee в python - PullRequest
0 голосов
/ 18 марта 2020

У меня есть коллекция 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='')

Я пытался «объединить» функции этих 2 фрагментов кода, но я не могу понять это , как собрать его вместе.

Что мне нужно, так это репликация поведения EXACT команды tee в файле сценария python. что означает ...

  • содержимое терминала появляется в окне терминала И , сохраненное в лог-файл (как решение A)

  • когда я запускаю файл сценария python, я хочу следить за ходом процесса в терминале, чтобы проверить, как далеко от завершения (как в решении B). Я не хочу смотреть на пустой экран, пока процесс не завершится (решение A).

Буду признателен за помощь.

для тестирования я использую файл формата webm (скачано с youtube-dl) и конвертируйте его в mp3 с помощью ffmpeg в cygwin. Вы можете скачать бинарный файл ffmpeg отсюда, если хотите поэкспериментировать с ним https://www.ffmpeg.org/download.html

Спасибо!

Ответы [ 2 ]

1 голос
/ 18 марта 2020

Вы читаете построчно, но 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)
1 голос
/ 18 марта 2020

Я провел некоторое тестирование, и нет, sys.stdout.flu sh () не решает эту проблему. Проблема, по-видимому, заключается в самой реализации Popen / PIPE - способ, которым он устанавливает каналы между подпроцессом и вашим процессом, вводит буферизацию.

Что, похоже, исправляет это:

в среде, из которой вы запускаете скрипт 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 .

...