подпроцесс python с shell = True: перенаправления и независимое от платформы уничтожение подпроцесса - PullRequest
6 голосов
/ 11 октября 2010

Мне трудно получить то, что я хочу, из модуля подпроцесса python (который предполагал унифицированную / независимую от платформы абстракцию, аааик, но не начинайте меня с этого:)).

Итак, простая вещь, которую я ищу, заключается в следующем.Я хочу

  • Запустить внешнее (stdio) приложение (возможно, с подпроцессом), где я использую перенаправления в стиле оболочки (например, './myapp> stdout_log> stderr_log')
    • По сути, я хочу выполнить командную строку оболочки, поэтому я должен указать shell = True для подпроцесса.Popen () (иначе перенаправления в командной строке не будут работать)
  • Я хочу запустить эту командную строку в асинхронном режиме (поэтому она запускается как независимый подпроцесс, но мой процесс Python не будет ждать его завершения)
  • (Мой родительский процессПроцесс Python время от времени просматривает журналы дочернего процесса для извлечения информации, но это не имеет отношения к вопросу)
  • Если мой родительский процесс Python решит, он сможет прервать этот дочерний процесс.

Теперь мои главные проблемы заключаются в том, что

  • Я вынужден использовать shell = True , чтобы заставить работать перенаправления
  • Обработка дочернего stdout / stderr в pпроцесс python не подходит, так как я не смог найти функциональные возможности для выполнения этого без ожидания (и родительский процесс python должен делать другие вещи во время работы дочернего элемента)
  • Если я использую shell = True , то subprocess.kill () будет завершать только оболочку, но не дочерний процесс
  • Мне нужен надежный метод завершения дочернего процесса, который работает на любомплатформа (но по крайней мере Linux и Windows)

Я надеюсь, что я был достаточно конкретным.Спасибо за любые советы / подсказки заранее - я просто провел целый день с подпроцессом, и ИМХО это боль далеко не от платформы или от простого :( (но, возможно, это только я)

ОБНОВЛЕНИЕ (2010-10-13):

Если вы запустите подпроцесс (даже с shell = False), то функция subprocess.Popen.kill () убьет только этот подпроцесс(поэтому, если есть какие-либо процессы «внуков», они не будут прекращены.)

Я читал об использовании параметра preexec_fn для установки sid во всех дочерних процессах, но он только для unix: тайм-аут подпроцесса

Ответы [ 4 ]

4 голосов
/ 11 октября 2010

В прошлый раз, когда я находился в подобной ситуации, я обнаружил, что самым простым (и практически единственным) решением было запустить нить, которая заботится о вашем дочернем процессе. С помощью этого метода вы можете использовать разные маршруты, будь то для анализа конвейера команды shell-style и выполнения в коде python (который, как вы сказали, не был возможен из-за блокировки), который одновременно исправит проблема убийства По сути, инкапсуляция потоков выглядит как путь.

К сожалению, мой опыт работы с subprocess полностью связан с платформой Windows, в которой есть тонны своих маленьких причуд. Кажется, у subprocess есть много недостатков, хотя он должен хорошо работать, учитывая наличие модулей popen, popen2 и т. Д., Которые он должен заменить.

3 голосов
/ 03 апреля 2013

У меня тоже недавно была похожая ситуация, я создал подпроцесс python, который использует shell=True, и позже мне нужно было его убить. Однако из-за параметра оболочки «реальный» процесс является потомком оболочки, то есть внуком основного процесса. Убийство дочерней оболочки автоматически не убивает внука.

В моем скрипте Python верхнего уровня:

childProcess = subprocess.Popen('python other.py', shell=True)

Чтобы убить оболочку и внука «настоящей работы», я сделал это:

subprocess.call("ps -ef | awk '$3 == \"" + str(childProcess.pid) + "\" {print $2}' | xargs kill -9", shell=True)
childProcess.kill()

Первая строка убивает всех дочерних процессов (на основе ps -ef происхождение, вторая строка убивает дочерних.

Интересно, что это необходимо в Ubuntu, но не обязательно в Mac OSX, так как на Mac внук, кажется, получает исходный идентификатор процесса дочерней оболочки.

3 голосов
/ 11 октября 2010

Ответы на некоторые вопросы:

  • Если вы хотите работать со стандартным выводом и вводом из команды, которая запускается как часть подпроцесса, вы должны использовать «shell = true»"

  • Конкретное решение может зависеть от того, что вы пытаетесь сделать.Тем не менее, для обработки вывода из команды, которая будет выполняться в течение длительного времени, есть несколько полезных решений

  • Экземпляр Popen предоставляет атрибут pid с идентификатором процесса дочернего процесса в виде shell = True.Вы должны быть в состоянии собрать командный pid и использовать его с os.kill ()

2 голосов
/ 11 октября 2010

Рассматривая ваши проблемы по одному:

Я вынужден использовать shell = True, чтобы заставить работать перенаправления

Вы не можетепросто используйте параметры stdout и stderr?

out_log = open("stdout_log", "w")
err_log = open("stderr_log", "w")
subproc = subprocess.popen(..., stdout=out_log, stderr=err_log, ...)

Обработка дочернего stdout / stderr в родительском процессе python - не вариант, так как я не смог найти функциональные возможности для этогобез ожидания (и родительский процесс python должен выполнять другие действия во время работы дочернего элемента)

Это из-за Windows.В ОС Unix-типа вы просто используете модуль select .Windows может только select для сокетов, но не для файлов.

Если я использую shell = True, то subprocess.kill () будет завершать только оболочку, но не дочерний процесс

Потому что, когда shell=True оболочка является дочерним процессом, а команды - его дочерними.

Мне нужен надежный метод завершения дочернего процесса, который работает на любомплатформа (но по крайней мере Linux и Windows)

Существует ли надежный метод завершения дочернего процесса для Windows?Последнее, что я слышал, даже конечная задача диспетчера задач не была надежной на 100%.А в Linux вы, возможно, не сможете убить процесс, у которого файл открыт через сбойный драйвер.

Как сказал Стигма, для поддержки Windows вам потребуется использовать потоки в качестве прокси подпроцесса.Кроме того, вы должны попытаться запустить с shell=False, и если вы не можете, объясните, почему нет.

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