Подпроцесс Python получает вывод детей в файл и терминал?

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

subprocess.call(cmdArgs,stdout=outf, stderr=errf)

когда _2 _ / _ 3_ либо None, либо дескриптор файла (разные файлы для _4 _ / _ 5_).

Есть ли способ выполнить каждый exe, чтобы stdout и stderr были записаны в файлы и терминал вместе?


person user515766    schedule 13.02.2011    source источник


Ответы (2)


Функция call() - это просто _ 2_. Вы можете вызвать Popen напрямую и использовать аргумент stdout=PIPE для чтения из p.stdout:

#!/usr/bin/env python
import sys
from subprocess import Popen, PIPE
from threading import Thread


def tee(infile, *files):
    """Print `infile` to `files` in a separate thread."""

    def fanout(infile, *files):
        with infile:
            for line in iter(infile.readline, b""):
                for f in files:
                    f.write(line)

    t = Thread(target=fanout, args=(infile,) + files)
    t.daemon = True
    t.start()
    return t


def teed_call(cmd_args, **kwargs):
    stdout, stderr = [kwargs.pop(s, None) for s in ["stdout", "stderr"]]
    p = Popen(
        cmd_args,
        stdout=PIPE if stdout is not None else None,
        stderr=PIPE if stderr is not None else None,
        **kwargs
    )
    threads = []
    if stdout is not None:
        threads.append(
            tee(p.stdout, stdout, getattr(sys.stdout, "buffer", sys.stdout))
        )
    if stderr is not None:
        threads.append(
            tee(p.stderr, stderr, getattr(sys.stderr, "buffer", sys.stderr))
        )
    for t in threads:
        t.join()  # wait for IO completion
    return p.wait()


outf, errf = open("out.txt", "wb"), open("err.txt", "wb")
assert not teed_call(["cat", __file__], stdout=None, stderr=errf)
assert not teed_call(["echo", "abc"], stdout=outf, stderr=errf, bufsize=0)
assert teed_call(["gcc", "a b"], close_fds=True, stdout=outf, stderr=errf)
person jfs    schedule 13.02.2011
comment
спасибо за быстрый ответ, но не работает. внешний процесс видит только дескрипторы файлов на уровне ОС (число, которое вы получаете из метода fileno () для ваших файловых объектов). см. bytes.com/topic/python/answers/541085-extend-file -type - person user515766; 13.02.2011
comment
спасибо, что бы вы сделали, если бы вместо subprocess.Call я хотел бы запустить несколько execs с помощью subprocess.Popen (а не Call), где каждый exec записывает в другой файл и на терминал - person user515766; 14.02.2011
comment
@ user515766: решение то же самое: установите stdout, stderr на PIPE и вызовите tee(), если вы хотите написать более чем в одно место. - person jfs; 14.02.2011
comment
кто-то удалил комментарии, демонстрирующие, что первый комментарий (не работает) неправильный. Это сбивает с толкуsubprocess.call и функцию call (другую), которая теперь называется teed_call, чтобы избежать двусмысленности. - person jfs; 24.10.2018
comment
@Peilonrayz: Я сделал код в ответе совместимым с Python 2/3. (это было чистое решение Python 2 в 2011 году) - person jfs; 15.12.2019
comment
Мне пришлось добавить это, чтобы пример заработал: if isinstance(f, io.TextIOBase): f.write(str(line)) else: f.write(line) - person channon; 03.11.2020
comment
@channon: str, скорее всего, неправильно. Обратите внимание, код в ответе открывает файлы в двоичном режиме. Если вам нужны текстовые файлы поддержки, укажите кодировку символов, соответствующую вашему регистру, такую ​​как utf-8, cp437 и т. Д. (Или передайте text=True, если вы хотите, чтобы модуль subprocess выбрал его за вас) $ Кроме того, следует использовать sys.stdout.buffer вместо sys.stdout в двоичном режиме на Python 3 (возможно, эта часть вызывает ошибку) - person jfs; 03.11.2020
comment
Да, вы правы, лучше использовать thesys.stdout.buffer. Теперь предоставленные тестовые примеры проходят, как и ожидалось. - person channon; 03.11.2020

Вы можете использовать что-то вроде этого: https://github.com/waszil/subpiper

В ваших обратных вызовах вы можете делать все, что хотите, регистрировать, записывать в файл, печатать и т. Д. Он также поддерживает неблокирующий режим.

from subpiper import subpiper

def my_stdout_callback(line: str):
    print(f'STDOUT: {line}')

def my_stderr_callback(line: str):
    print(f'STDERR: {line}')

my_additional_path_list = [r'c:\important_location']

retcode = subpiper(cmd='echo magic',
                   stdout_callback=my_stdout_callback,
                   stderr_callback=my_stderr_callback,
                   add_path_list=my_additional_path_list)
person waszil    schedule 22.05.2019
comment
Я пробовал это с помощью cmd = 'python3 run2.py', но получил сообщение об ошибке. Мне что-то не хватает, как отправить run2.py в качестве параметра: FileNotFoundError: [Errno 2] Нет такого файла или каталога: 'python3 run2.py': 'python3 run2.py' - person Rimfire; 12.08.2019