2014-11-05 2 views
1

Я хочу использовать subprocess.Popen для запуска процесса со следующими требованиями.Нестандартные трубы Popen

  1. Я хочу конвейерный stdout и stderr обратно к вызывающему Popen, как проходит процесс.

  2. Я хочу убить процесс после timeout секунд, если он все еще работает.

Я пришел к выводу, что недостаток в subprocess API означает, что он не может выполнить эти два требования одновременно. Рассмотрим следующие игрушечные программы:

chatty.py

while True: 
    print 'Hi' 

silence.py

while True: 
    pass 

caller.py

import subprocess 
import time 

def go(command, timeout=60): 
    proc = subprocess.Popen(command, shell=True, 
          stdout=subprocess.PIPE, stderr=subprocess.PIPE) 
    start = time.time() 
    while proc.poll() is None: 
     print proc.stdout.read(1024) # <----- Line of interest 
     if time.time() - start >= timeout: 
      proc.kill() 
      break 
     else: 
      time.sleep(1) 

Рассмотрим отмеченную строку выше.

  • Если он включен, go('python silence.py') будет висеть вечно - не только 60 секунд - потому что read является блокировка вызова до либо 1024 байт или конца потока, и ни один никогда не приходит.

  • Если будет прокомментировано, go('python chatty.py') будет распечатывать 'Hi' снова и снова, но как он может быть передан обратно по мере его создания? proc.communicate() блокируется до конца потока.

Я был бы счастлив с решением, которое заменяет требование (1) выше «В том случае, когда тайм-аут не произойдет, я хочу, чтобы получить stdout и stderr раз алгоритма заканчивается.» Даже это было проблематично. Моя попытка внедрения приведена ниже.

speech.py ​​

for i in xrange(0, 10000): 
    print 'Hi' 

caller2.py

import subprocess 
import time 

def go2(command, timeout=60): 
    proc = subprocess.Popen(command, shell=True, 
          stdout=subprocess.PIPE, stderr=subprocess.PIPE) 
    start = time.time() 
    while True: 
     if proc.poll() is not None: 
      print proc.communicate() 
      break 
     elif time.time() - start >= timeout: 
      proc.kill() 
      break 
     else: 
      time.sleep(1) 

Но даже это все еще есть проблемы. Хотя python speech.py работает всего за пару секунд, go2('python speech.py') занимает 60 секунд. Это связано с тем, что вызов print 'Hi' в speech.py блокируется до тех пор, пока не будет вызван proc.communicate(), когда процесс будет убит. С proc.stdout.read была проблема, продемонстрированная ранее с silence.py, я действительно не понимаю, как это сделать.

Как я могу получить как stdout, так и stderr и поведение таймаута?

ответ

1

Хитрость заключается в настройке таймера боковой полосы, чтобы убить процесс.Я написал программу на полпути между болтливым и бесшумным:

import time 
import sys 

for i in range(10,0,-1): 
    print i 
    time.sleep(1) 

, а затем программой, чтобы убить его раньше:

import subprocess as subp 
import threading 
import signal 

proc = subp.Popen(['python', 'longtime.py'], stdout=subp.PIPE, 
    stderr=subp.PIPE) 
timer = threading.Timer(3, lambda proc: proc.send_signal(signal.SIGINT), 
    args=(proc,)) 
timer.start() 
out, err = proc.communicate() 
timer.cancel() 
print proc.returncode 
print out 
print err 

и выход:

$ python killer.py 
1 
10 
9 
8 

Traceback (most recent call last): 
    File "longtime.py", line 6, in <module> 
    time.sleep(1) 
KeyboardInterrupt 

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

+0

Выглядит многообещающе. Попытка этого завтра. –

Смежные вопросы