подпроцесс stderr против sys.stderr - PullRequest
0 голосов
/ 12 октября 2018

Я не уверен, является ли вопрос вопросом Python или оболочки.

У меня есть программа Python, которая использует вызов подпроцесса для команды, которая может выдать сообщение об ошибке в stderr.Моя собственная программа также использует sys.stderr для регистрации ошибок.Вот простой пример с командой (ls * .foobar), которая не работает:

import sys,subprocess

sys.stderr.write("--Hello\n")
try:
    subprocess.check_call("ls *.foobar",shell=True)
except subprocess.CalledProcessError as e:
    sys.stderr.write("Command failed\n")
sys.stderr.write("--Bye\n")

Когда я запускаю этот код, выход на консоли (из stderr) выглядит следующим образом:

- Привет
ls: невозможно получить доступ к * .foobar: такой файл или каталог отсутствуют
Команда не выполнена
--Bye

Если я перенаправлю stderr вфайл (например, используя python myscript.py 2> log), файл содержит следующее:

ls: невозможно получить доступ к * * .foobar ': такого файла или каталога нет
--Hello
Команда не выполнена
--Bye

Есть ли способ сохранить порядок сообщений в файле (кроме использования явного перенаправления stderr на файл в вызове подпроцесса)?

Эта проблема напоминает некоторые стандартные проблемы stdout / stderr, но здесь все должно быть на stderr.

1 Ответ

0 голосов
/ 12 октября 2018

Вам необходимо очистить модуль записи данных в дескриптор stderr:

sys.stderr.write("--Hello\n")
sys.stderr.flush()

stdio - это буферизация строки при подключении к терминалу и использование фиксированного буфера при подключении ктруба.Вы пишете \n символ новой строки, который вызывает сброс при подключении к терминалу, но без этого для каждой строки вы не пишете достаточно в stderr, чтобы вызвать сброс буфера перед окончательным сбросом при выходе из Python.

Если вы используете print(..., file=sys.stderr), вы можете указать print(), чтобы выполнить вызов flush(), добавив flush=True:

print("--Hello", file=sys.stderr, flush=True)

Другой способ справиться с этим -перехватить и освободить вывод stderr дочернего процесса:

sys.stderr.write("--Hello\n")
try:
    subprocess.check_call("ls *.foobar", shell=True, stderr=subprocess.PIPE)
except subprocess.CalledProcessError as e:
    sys.stderr.write(e.stderr)
    sys.stderr.write("Command failed\n")
sys.stderr.write("--Bye\n")

Добавление stderr=subprocess.PIPE сообщает subprocess о захвате вывода команды stderr, и вы можете найти этот вывод как e.stderrатрибут исключения CalledProcessError.

...