#python #subprocess
#python #подпроцесс
Вопрос:
Я запускаю двоичный исполняемый файл из Python с помощью subprocess.run()
команды, и команда выдает около 20 МБ текстовых данных в стандартный вывод. Меня интересуют только первые несколько строк вывода, и загрузка всего вывода команды в память занимает очень много времени (около 10 секунд).
Я хотел бы прочитать стандартный вывод команды до 10-й строки, а затем обрезать все остальные выходные данные. Чего я хотел бы достичь, так это эквивалентности running command | head
(что очень быстро), но у меня есть shell=False
набор, который не позволяет использовать каналы.
Есть ли какой-либо способ, которым я могу усечь вывод стандартного вывода всего до нескольких строк / байтов, не загружая все это в память? Я уже пробовал bufsize=
параметр, но он не имел никакого эффекта.
Комментарии:
1. Все
head
, что нужно, это прочитать заданное количество строк из стандартного вывода процесса, а затем выйти (после чего программа в левой части конвейера получает SIGPIPE при следующей попытке записи в свой стандартный вывод и, следовательно, обычно также завершает работу). Вы можете, конечно, сделать это самостоятельно — в своем коде на Python.2. …то есть: Мне нужно было бы просмотреть ваш конкретный код
subprocess
, чтобы узнать, почему он загружает весь стандартный вывод в память. Это не безусловное / автоматическое / поведение по умолчанию, если только вы не используете что-то вродеcommunicate()
, которое явно определено для этого.3. Спасибо, что объяснили
head
мне поведение. Я думаю, что в моем случае проблема не в том, что все это находится в памяти, а в том, чтоhead
удается уничтожить команду после того, как она выдает 10 строк, но подпроцесс будет ждать, пока команда не завершится полностью.4. Хотелось бы, чтобы был способ воссоздать то, что
head
вы объяснили выше5.
head
на самом деле не завершает программу; все, что он делает, это закрывает выходную часть FIFO (неявно, путем выхода), программа убивает себя, когда ее следующая попытка записать что-либо в стандартный вывод завершается неудачей. Отправка SIGPIPE выполняется операционной системой автоматически, когда естьread
системный вызов после того, как автор закрыл свой конец, илиwrite
системный вызов после того, как читатель закрыл свой конец.
Ответ №1:
При использовании subprocess.Popen
вы можете получить доступ к стандартному выводу подпроцесса и прочитать столько строк, сколько захотите
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
lines_to_read = 10
for i in range(lines_to_read):
print(p.stdout.readline())
Комментарии:
1. Это по-прежнему загружает все это в память и занимает около 10 секунд для моей команды, в отличие от
command | head
команды, которая занимает ~ 100 мс2. 10 секунд для достижения какой точки? Конец цикла или вы ждете, что после завершения цикла произойдет что-то еще? (Если вы поместите
p.wait()
туда a , f / e, это вполне может закончиться зависанием на неопределенный срок, поскольку дочерний процесс все равно будет висеть в ожидании чтения его выходных данных).3. @TadejMagajna, … кстати, вы приняли это после подтверждения того, что ввод
p.stdout.close()
после цикла работает? (Если это так, это может стоить отметить, чтобы к ответу можно было добавить явное закрытие).4. Нет, я принял это после фактического запуска кода (изначально я был убежден, что описанный выше подход не будет работать до его фактического запуска). Отлично работает — спасибо вам обоим!