Читать данные с stderr subprocess.Popen

def record():
    command = 'ffmpeg -loglevel warning -hide_banner -y -f gdigrab -framerate 30 -i desktop output.mkv'
    command = subprocess.Popen(command, stdin=subprocess.PIPE)
    sleep(5)
    command.stdin.write('q'.encode('utf-8'))
    command.stdin.flush()
    command.wait()

У меня есть вот такая простенькая функция. Она запускает команду, которая записывает экран, спит 5 секунд и останавливает команду. В конце генерируется файл output.mkv. В этот файл ffmpeg кладёт видео с записью экрана. Если я заменю output.mkv, на output.img, то ffmpeg выдаст ошибку: 'Неверный формат выходного файла' Как я могу читать stderr, чтобы поймать эту ошибку, если она, конечно же, есть?


Ответы (2 шт):

Автор решения: Victor VosMottor
command = subprocess.Popen(command, stdin=subprocess.PIPE)
out, err = command.communicate()
print(err)
→ Ссылка
Автор решения: Pak Uula

В ваш код достаточно добавить две фичи:

  1. При создании процесса задать параметр stderr конструктора Popen равным subprocess.PIPE,
  2. И в отдельном потоке читать созданный файловый объект. Если ничего не прочитается, значит, ошибки не было.

Если что-то прочитается, то либо сразу выставить флаг ошибки, либо попробовать проанализировать прочитанное.

Простейший вариант может выглядеть вот так:

import subprocess
import threading

failed = False


def read_stderr(stderr_pipe):
    "Sets global variable `failed` if stderr is not empty"
    global failed
    while True:
        try:
            smth = stderr_pipe.read(1)
            # Use readline if you want to parse error messages
            # smth = stderr_pipe.readline()
        except err:
            # reading failed - something went wrong with process construction
            return

        if len(smth) != 0:
            # smth is read from stderr
            failed = True
            return
        else:
            # the pipe is empty - the process terminated without writing to stderr
            pass
            return

def record():
    "Returns `True` if the process runs without errors and `False` otherwise."
    # command_line = ["ffmpeg", "-loglevel", "warning", "-hide_banner", "-y", "-f", "gdigrab", "-framerate", "30", "-i", "desktop", "output.mkv"]
    command_line = ["ls", "/no/such/file"]
    # for testing: successfull command
    # command_line = ["ls", "/bin/bash"]
    
    command = subprocess.Popen(command_line, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
    t = threading.Thread(target=read_stderr, args=(command.stderr,))
    t.start()

    # sleep(5)
    # command.stdin.write('q'.encode('utf-8'))
    command.stdin.flush()
    command.wait()
    
    t.join()
    global failed
    return not failed
    

success = record()
print("Success: ", success)

Для тестирования я использовал команды ls /no/such/file и ls /bin/bash. В первом случае вывод был

Success:  False

во втором

/bin/bash
Success:  True
→ Ссылка