поймать вывод подпроцесса, порожденный импортированным модулем - PullRequest
0 голосов
/ 26 февраля 2020

Примечание: я знаю, что могу поймать subprocess вывод, используя stdout=subprocess.PIPE.

В моем случае subprocess будет запущен импортированным модулем, например, youtube-dl запустит subprocess для ffmpeg, и я хочу перехватить ffmpeg вывод.

Я сделал фиктивный рабочий сценарий, чтобы прояснить мою точку зрения.

У меня есть 2 файла: m1.py "мой модуль" и m2.py "импортированная библиотека"

m2.py

import subprocess
def run():
    print('m2 module message will be caught by echo function too')
    cmd = ['ping', 'google.com']
    subprocess.Popen(cmd)

m1.py

import sys
import m2

# this is a redirection function to echo stdout to a file
def echo(text):
    with open('out.txt', 'a') as f:
        f.write(text)

    sys.stdout.write = original
    sys.stdout.write(text)
    sys.stdout.write = echo

original = sys.stdout.write
sys.stdout.write = echo

print('m1 module message will be caught by echo function')
m2.run()

Терминальный выход:

m1 module message will be caught by echo function
m2 module message will be caught by echo function too

Pinging google.com [172.217.18.46] with 32 bytes of data:
Reply from 172.217.18.46: bytes=32 time=86ms TTL=53
Reply from 172.217.18.46: bytes=32 time=78ms TTL=53

out.txt

m1 module message will be caught by echo function
m2 module message will be caught by echo function too

сверху видно, что из TXT-файл получил 2 оператора печати от обоих модулей, но не может получить другой вывод

Вопрос здесь, как я могу получить вывод команды «ping», запущенной m2.py, «точно не могу измените код m2.py, потому что это сторонняя библиотека, и она обновляется еженедельно "

Ответы [ 2 ]

1 голос
/ 26 февраля 2020

Примечание. Это может быть только частичный ответ, поскольку это низкий уровень операционной системы. У меня это работало на linux, но я не могу дать утверждение относительно windows или другого os

Попытка перенаправить вывод с помощью вашей функции echo() не работает, потому что это только переопределение write в вашем python процессе. Команда ping (или любая другая команда, запущенная вашим сторонним модулем) - это новый процесс, который не наследует вашу среду python, в частности не переписанную функцию write().

Но - при создании нового процесса он наследует дескриптор файла stdout (и я имею в виду дескриптор файла низкого уровня os, который равен 1 для stdout на unix)

Я мог бы перенаправить вывод ping с помощью следующего кода.

import os

...
out2 = open('out2.txt', 'w')
fd = out2.fileno()  # here you get the low level file handle of the new file obj
os.dup2(fd, 1)      # here you are closing your stdout handle and setting it to out2

...
m2.run()     # the spawned ping command is now writing to out2

Теперь это перенаправление на файловый объект. Я понимаю, что вы хотите прочитать вывод в вашей программе.

Для этого вы можете создать канал в python и использовать вместо этого этот дескриптор файла:

pipein, pipeout = os.pipe()

Примечание уже низкоуровневые файловые дескрипторы. Чтобы использовать один из них как файловый объект python, вы можете создать его с помощью os.fdopen(fd)

0 голосов
/ 26 февраля 2020

Просто верните результат прогона subprocess и присвойте его обычно

м2

import subprocess
def run():
    print('m2 module message will be caught by echo function too')
    cmd = ['ping', 'google.com']
    return subprocess.Popen(cmd)

m1

import sys
import m2

# this is a redirection function to echo stdout to a file
def echo(text):
    with open('out.txt', 'a') as f:
        f.write(text)

    sys.stdout.write = original
    sys.stdout.write(text)
    sys.stdout.write = echo

original = sys.stdout.write
sys.stdout.write = echo

print('m1 module message will be caught by echo function')
process_object = m2.run()
output, err = process_object.communicate()
print(output, err)

Надеюсь, это поможет

...