2009-05-17 10 views
44

Chciałbym użyć modułu subprocess w następujący sposób:Jak mogę dostać „w czasie rzeczywistym” informacji z powrotem z subprocess.Popen w python (2.5)

  1. utworzyć nowy proces, który potencjalnie wykonanie zajmuje dużo czasu.
  2. przechwytywania stdout (lub stderr lub potencjalnie obu, razem lub osobno)
  3. dane procesowe z podproces jak to przychodzi, może zapłonów na każdej linii otrzymały (w wxPython powiedzieć), albo po prostu ich drukowania Na razie.

Stworzyłem procesy z Popenem, ale jeśli używam komunikacji(), dane przychodzą do mnie wszystkie naraz, gdy proces się zakończy.

Jeśli utworzę osobny wątek, który blokuje readline() z myprocess.stdout (używając stdout = subprocess.PIPE), nie otrzymam żadnych linii z tą metodą, dopóki proces nie zostanie zakończony. (bez względu na ustawienie bufsize)

Czy istnieje sposób radzenia sobie z tym, który nie jest horrendalny i działa dobrze na wielu platformach?

+2

myprocess.stdout.readline() powinien działać. Czy możesz pokazać nam swój kod? –

+0

Niebuforowane odczyty z popen_obj.stdout() powinny rzeczywiście działać - ale jeśli nie masz nic przeciwko ograniczeniu do platform z obsługą PTY, twoja aplikacja może być odpowiednia dla biblioteki Pexpect. –

+1

To jest świetne pytanie i nadal wydaje się być bez odpowiedzi, przynajmniej w przypadku wymogu "działa dobrze na wielu platformach". –

Odpowiedz

7

stdout będzie buforowany - więc nic nie dostaniesz, dopóki ten bufor nie zostanie wypełniony lub podproces zostanie zamknięty.

Możesz spróbować przepłukać stdout z podprocesu lub używając stderr lub zmienić stdout w trybie bez buforowania.

+2

Czy domyślnie nie powinien być domyślnie buforowany? Przynajmniej z bufsize = 0? – Albert

+2

@Albert: bufor jest * wewnątrz * podprocesu np. Bufor 'stdio'. Nic poza procesem podrzędnym nie widzi tych danych, dopóki nie opróżni standardowego bufora. Oto niektóre rozwiązania problemu [problem z buforowaniem] (http://stackoverflow.com/a/20509641/4279) – jfs

2

Wygląda na to, że problemem może być użycie buforowanego wyjścia w podprocesie - jeśli zostanie utworzona stosunkowo niewielka ilość danych wyjściowych, można będzie buforować do czasu zakończenia podprocesu. Niektóre tła można znaleźć here:

8

Aktualizacja z kodem, który wydaje się nie działać (na windows i tak)

class ThreadWorker(threading.Thread): 
    def __init__(self, callable, *args, **kwargs): 
     super(ThreadWorker, self).__init__() 
     self.callable = callable 
     self.args = args 
     self.kwargs = kwargs 
     self.setDaemon(True) 

    def run(self): 
     try: 
      self.callable(*self.args, **self.kwargs) 
     except wx.PyDeadObjectError: 
      pass 
     except Exception, e: 
      print e 



if __name__ == "__main__": 
    import os 
    from subprocess import Popen, PIPE 

    def worker(pipe): 
     while True: 
      line = pipe.readline() 
      if line == '': break 
      else: print line 

    proc = Popen("python subprocess_test.py", shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE) 

    stdout_worker = ThreadWorker(worker, proc.stdout) 
    stderr_worker = ThreadWorker(worker, proc.stderr) 
    stdout_worker.start() 
    stderr_worker.start() 
    while True: pass 
+0

Jest to zdecydowanie najlepsza odpowiedź. Dzięki za pokazanie mi tego "potoku" w Pythonie! – Mapad

+0

To jest świetna odpowiedź i działa dla mnie. Kluczem jest to, że odczyty mogą blokować bez problemu z powodu wątków. –

+0

'while True: time.sleep (1)' jest lepsze niż pętla oczekiwania zajętości zużywająca cały procesor. – ReneSac

2

Oto co pracował dla mnie:

cmd = ["./tester_script.bash"] 
p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 
while p.poll() is None: 
    out = p.stdout.readline() 
    do_something_with(out, err) 

W twoim przypadku można spróbować przekazać odniesienie do procesu cząstkowego z pracownikiem wątek, a nie odpytywanie wewnątrz wątku. Nie wiem, jak zachowa się, gdy dwa wątki odpytywają (i wchodzą w interakcje) w tym samym podprocesie, ale może działać.

Należy również zauważyć, że numer while p.poll() is None: jest taki, jaki jest. Czy nie zamień go na while not p.poll() jak w pytonie 0 (kod powrotu do pomyślnego zakończenia) jest również uważany za False.

+0

Nie jestem pewien, czy czegoś tu brakuje, ale wygląda na to, że Twój kod rzeczywiście blokuje się: pętla "while" oznacza, że ​​niezależnie od tego, jaki powinien być wynik tej funkcji, nie zostanie ona zwrócona do czasu p.poll() jest Brak. – pgcd

+0

Tak. To się blokuje. Przypuszczam, że kiedy odpowiedziałem na to pytanie, nie zauważyłem części wątku. Mimo to moja odpowiedź tutaj nie jest całkiem "aktualna". Głównym problemem jest to, że dane wyjściowe są buforowane (jak wspomniano w innym miejscu) i nie wspominam o tym tutaj. Zostawię to tutaj dla potomności, ale inne przesłane odpowiedzi są lepsze. – exhuma

1

Występuję również w tym problemie. Problem występuje, ponieważ próbujesz przeczytać również stderr. Jeśli nie ma błędów, próba odczytu ze stderr zablokuje.

W systemie Windows nie ma łatwego sposobu odpytywania() deskryptorów plików (tylko gniazda Winsock).

Rozwiązaniem nie jest więc próba odczytu ze stderr.

1

Użycie pexpect [http://www.noah.org/wiki/Pexpect] z nieblokującymi liniami odczytu rozwiąże ten problem. Wynika to z faktu, że rury są buforowane, a więc dane wyjściowe aplikacji są buforowane przez potok, dlatego nie można uzyskać tego wyjścia, dopóki bufor się nie wypełni lub proces nie zostanie przerwany.

1

Wydaje się, że jest to dobrze znane ograniczenie w języku Python, zobacz PEP 3145 i może inne.

1

Przeczytaj jeden znak naraz: http://blog.thelinuxkid.com/2013/06/get-python-subprocess-output-without.html

import contextlib 
import subprocess 

# Unix, Windows and old Macintosh end-of-line 
newlines = ['\n', '\r\n', '\r'] 
def unbuffered(proc, stream='stdout'): 
    stream = getattr(proc, stream) 
    with contextlib.closing(stream): 
     while True: 
      out = [] 
      last = stream.read(1) 
      # Don't loop forever 
      if last == '' and proc.poll() is not None: 
       break 
      while last not in newlines: 
       # Don't loop forever 
       if last == '' and proc.poll() is not None: 
        break 
       out.append(last) 
       last = stream.read(1) 
      out = ''.join(out) 
      yield out 

def example(): 
    cmd = ['ls', '-l', '/'] 
    proc = subprocess.Popen(
     cmd, 
     stdout=subprocess.PIPE, 
     stderr=subprocess.STDOUT, 
     # Make all end-of-lines '\n' 
     universal_newlines=True, 
    ) 
    for line in unbuffered(proc): 
     print line 

example() 
+0

wydaje się być duplikatem odpowiedzi. Zobacz [mój poprzedni komentarz] (http: // stackoverflow.com/questions/803265/get-realtime-output-using-subprocess/15288921 # comment41473849_15288921) – jfs

0

Korzystanie subprocess.Popen mogę uruchomić .exe jednego z moich projektów C# i przekierować wyjście do mojego pliku Pythona. Mogę teraz do print() wszystkie informacje są wyprowadzane do konsoli C# (przy użyciu Console.WriteLine()) do konsoli Python.

kod Python:

from subprocess import Popen, PIPE, STDOUT 

p = Popen('ConsoleDataImporter.exe', stdout = PIPE, stderr = STDOUT, shell = True) 

while True: 
    line = p.stdout.readline() 
    print(line) 
    if not line: 
     break 

ten dostaje wyjścia konsoli mojego .NET linię projektową po linii, ponieważ jest tworzony i wybucha z załączania podczas pętli na zakończenie projektu. Wyobrażam sobie, że to też działa dla dwóch plików Pythona.

Powiązane problemy