Как управлять подпроцессом в Python, когда вывод огромен? - PullRequest
1 голос
/ 03 августа 2010

Я управляю длительным моделированием (часами, днями, даже неделями), используя скрипт bash, который перебирает все нужные параметры. Если одновременно выполняется только одно моделирование, выходные данные передаются в «tee», в противном случае выходные данные просто передаются «>» в ​​выходной файл. Весь вывод огромен: некоторые файлы журналов ~ 2 ГБ и могут быть еще больше.

Скрипт работает, но поддерживать его очень просто. Когда мы добавляем новый параметр, требуется некоторое время, чтобы адаптировать скрипт и весь sed-foo в нем. Так что я перенес это на Python. Это работает БОЛЬШОЙ.

Единственная проблема, которая у меня сейчас мешает использовать ее в работе, заключается в том, что я не могу найти правильный способ вызова Popen () для запуска программы. Если я запускаю его «беззвучно», передавая все в файл и не показывая никакого вывода, python берет гигабайты оперативной памяти до того, как симуляция будет завершена.

Вот фрагмент кода:

fh = open(logfile, "w")
pid = subprocess.Popen(shlex.split(command), stdout=fh)
pids.append(pid)

Я читал много материала о выводе Popen, но я думал, что при его передаче в файл при необходимости очистится буфер?

Может быть, подпроцесс Popen () не самый лучший для этого? Каков наилучший способ показать и сохранить выходные данные программы на экране и в файле, не занимая все оперативной памяти?

Thanx!

Ответы [ 4 ]

2 голосов
/ 03 августа 2010

Почему бы не записать в файл без вывода сообщений, а затем tail it?

Вы можете использовать file.flush() для очистки буфера файлов Python.


Python будет рад обрабатывать новые строки в текущем открытом файле. Например:

f = open( "spam.txt", "r" )
f.read()
# 'I like ham!'
# Now open up spam.txt in some other program and add a new line.
f.read()
# 'I like eggs too!'
0 голосов
/ 16 мая 2012

Вместо того, чтобы разбивать вывод вашей симуляции, выберите передать его в файл (или запишите в файл из симуляции, затем используйте tail -f, чтобы просмотреть последний вывод в консоли.

Пример симуляции:

#!/bin/bash

while true; do
  echo $$ -- $(date +%s)
  sleep 1
done

Или, возможно:

#!/usr/bin/env python
import os, sys, time

while True:
  sys.stdout.write("%d -- %d\n"%(os.getpid(), time.time()) )
  sys.stdout.flush()
  time.sleep(1)

Вызов:

$ nohup ./simulation &> logfile &

Просмотр вывода:

$ tail -f logfile
1285 -- 1337166243
1285 -- 1337166244
1285 -- 1337166245
1285 -- 1337166246
1285 -- 1337166247
^C

Примечания:

  • Бонусные баллы за разбиение stderr и stdout на разные лог-файлы.
  • Не используйте tee для подобных вещей. Это хрупко и будет приводить к ошибкамк вашей симуляции на случай, если что-то плохое случится в конце канала.
  • Обратите внимание, как мы записываем PID симуляции, чтобы мы могли прервать его, если захотим, после того, как он был запущен. Рекомендуется сохранить его вpid-файл вместо журнала симуляции, просто для простоты при уничтожении симуляции.
  • Использование nohup. Это защитит ваш прогон симуляции в случае, если вы закроете исходный терминал, или если X11 выйдет из строя (из опытаce, это произойдет, когда ваш 4-дневный симулятор завершен на 98%, и вы не установили контрольные точки ...).
0 голосов
/ 04 августа 2010

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

Посмотрите здесь некоторые примеры неблокирующих операций чтения из канала подпроцесса:

Как я могу прочитать все доступные данные из subprocess.Popen.stdout (без блокировки)?

0 голосов
/ 04 августа 2010

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

pipe_verbose = sys.stdout
pipe_silent  = open('/dev/null', 'w')

subprocess.Popen(shlex.split(command), stdout=pipe_silent)
subprocess.Popen(shlex.split(command), stdout=pipe_verbose)

и наконец я опрашиваю (), чтобы увидеть, когда это будет сделано.

Пайпинг имеет хороший результат: если я нажму ctrl + c скрипт, он тоже убьет работу. Если я не поместил stdout = ... в Popen (), то работа продолжится в фоновом режиме. Кроме того, загрузка процессора Python остается на уровне 0%. Цикл readline на трубе увеличит его до 100% ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...