Могу ли я разделить / объединить выходные потоки подпроцесса. Открыть? - PullRequest
7 голосов
/ 15 января 2012

Я пишу класс-оболочку для использования с менеджером рабочего процесса. Я хотел бы зарегистрировать вывод из приложения (дочерний процесс, выполняемый через subprocess.Popen) определенным образом:

  • stdout ребенка должны перейти в файл журнала и stdout родителя,
  • stderr ребенка должен перейти в другой лог-файл, но также stdout родителя.

т.е. все выходные данные дочернего элемента должны быть объединены в stdout (как в случае subprocess.Popen(..., stderr=subprocess.STDOUT), поэтому я могу зарезервировать stderr для сообщений журнала от самой оболочки. С другой стороны, дочерние потоки должны идти в разные файлы, чтобы отдельная проверка.

Я пытался использовать вспомогательный класс "Tee", чтобы связать два потока (stdout и файл журнала) вместе, чтобы Tee.write записывал в оба потока. Однако, это не может быть передано Popen, потому что «подпроцесс» использует функции уровня ОС для записи (см. Здесь: http://bugs.python.org/issue1631).

Проблема с моим текущим решением (фрагмент кода ниже, адаптированный в основном из здесь ) заключается в том, что выходные данные stdout могут отображаться не в правильном порядке.

Как я могу преодолеть это? Или я должен использовать совершенно другой подход? (Если я придерживаюсь приведенного ниже кода, как выбрать значение для количества байтов в os.read?)

import subprocess, select, sys, os

call = ... # set this
process = subprocess.Popen(call, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
logs = {process.stdout: open("out.log", "w"), process.stderr: open("err.log", "w")}
done = {process.stdout: False, process.stderr: False}
while (process.poll() is None) or (not all(done.values())):
    ready = select.select([process.stdout, process.stderr], [], [])[0]
    for stream in ready:
        data = os.read(stream.fileno(), 1)
        if data:
            sys.stdout.write(data)
            logs[stream].write(data)
        else:
            done[stream] = True
logs[process.stdout].close()
logs[process.stderr].close()

Кстати, это решение с использованием "fcntl" у меня не сработало. И я пока не мог понять, как адаптировать это решение к моему делу, поэтому я не пробовал.

1 Ответ

1 голос
/ 15 января 2012

Если вы установите shell=True, вы можете передать командную строку в подпроцесс , который включает в себя каналы, перенаправления и команду tee .

...