2010-06-29 8 views
12

Znów to samo pytanie.
Powodem jest - wciąż nie może pracować po przeczytaniu poniższych czynności:subprocess.Popen.stdout - czytanie stdout w czasie rzeczywistym (ponownie)

Moja sprawa jest, że mam aplikacja konsolowa napisana w języku C, pozwala wziąć na przykład ten kod w pętli:

tmp = 0.0; 
printf("\ninput>>"); 
scanf_s("%f",&tmp); 
printf ("\ninput was: %f",tmp); 

Ciągle odczytuje niektóre dane wejściowe i zapisuje niektóre dane wyjściowe.

Mój kod Pythona do interakcji z nim jest następujący:

p=subprocess.Popen([path],stdout=subprocess.PIPE,stdin=subprocess.PIPE) 
p.stdin.write('12345\n') 
for line in p.stdout: 
    print(">>> " + str(line.rstrip())) 
    p.stdout.flush() 

tej pory ilekroć czytam formularz p.stdout zawsze czeka, aż proces zostanie zakończony, a następnie wysyła pusty ciąg. Próbowałem wiele rzeczy - ale wciąż taki sam wynik.

Próbowałem Pythona 2.6 i 3.1, ale wersja nie ma znaczenia - po prostu muszę sprawić, żeby to działało gdzieś.

+1

Jestem w dokładnie tej samej sytuacji, po przeczytaniu wszystkich tych pytań i nadal nie znalazłem nic eleganckiego, co działa. –

+0

related: [Jak poprawnie współpracować z procesem za pomocą modułu podprocesowego] (http://stackoverflow.com/q/443057/4279) – jfs

+0

Przyszedłem tutaj z tym samym pytaniem, znalazłem odpowiedź tutaj http://stackoverflow.com/q/375427/168034 – phunehehe

Odpowiedz

3

Próba zapisu i odczytu z potoków do podprocesu jest trudna ze względu na domyślne buforowanie w obu kierunkach. Bardzo łatwo uzyskać zakleszczenie tam, gdzie jeden lub drugi proces (rodzic lub dziecko) czyta z pustego bufora, pisząc do pełnego bufora lub robiąc blokujący odczyt na buforze, który czeka na dane, zanim biblioteki systemowe go opróżnią.

W przypadku skromniejszych ilości danych może być wystarczająca metoda Popen.communicate(). Jednak w przypadku danych, które przekraczają jego buforowanie, prawdopodobnie utknęłyby w martwym punkcie procesy (podobne do tego, co już widzisz?).

Możesz chcieć sprawdzić szczegóły na temat korzystania z modułu fcntl i wykonać jedną lub drugą (lub oba) deskryptorów plików nie blokujących. W takim przypadku, oczywiście, będziesz musiał zawijać wszystkie odczyty i/lub zapisy do tych deskryptorów plików w odpowiedniej obsłudze wyjątków, aby obsłużyć zdarzenia "EWOULDBLOCK". (Nie pamiętam dokładnego wyjątku Python, który został podniesiony dla tych).

Zupełnie inne podejście byłoby dla twojego rodzica, aby użyć modułu select i os.fork() ... i dla procesu potomnego do execve() programu docelowego po bezpośrednim przetwarzaniu dup() ing pliku. (Zasadniczo można ponownie wdrożyć części Popen(), ale z inną obsługą nadrzędnego pliku deskryptora (PIPE):

Nawiasem mówiąc. Komunikowanie, przynajmniej w standardowych bibliotekach 2.5 i 2.6 Pythona, obsługuje tylko około 64 KB zdalnych danych (w systemach Linux i FreeBSD) Liczba ta może się różnić w zależności od różnych czynników (w tym opcji kompilacji używanej do kompilowania interpretera Pythona lub wersji biblioteki libc), która nie jest po prostu ograniczona dostępną pamięcią (pomimo JF Twierdzenie Sebastiana przeciwnie), ale ogranicza się do znacznie mniejszej wartości.

+0

Czy możesz podać przykładową implementację? Po prostu grałem trochę z modułem 'Queue', ale to w ogóle nie działa. – Philipp

+1

+1 za wzmiankę o buforowaniu jako głównym winowajcy, patrz także [Dlaczego nie po prostu użyć potoku (popen())?] (Http://www.noah.org/wiki/pexpect#Q:_Why_not_just_use_a_pipe_.28popen.28.29 .29.3F) z dokumentacji "pexpect". Ale odpowiedź jest zbyt pesymistyczna, tzn. '.communicate()' działałoby dla wszystkich danych, które zmieszczą się w pamięci (znacznie większe niż rozmiary buforów rur). Również 'fcntl' (prawdopodobnie) nie będzie działać w systemie Windows i nie jest to konieczne. – jfs

+0

['.communicate()' działa} (http://ideone.com/gQbDPT). Wcześniejsze wersje prawdopodobnie miały teraz błąd, który został naprawiony. – jfs

1

bufsize=256 argumentu zapobiega 12345\n przed wysłaniem do procesu są w postaci fragmentu mniejszy niż 256 bajtów, jak to będzie w przypadku pominięcia bufsize lub wstawienia p.stdin.flush() po p.stdin.write(). Domyślnym zachowaniem jest buforowanie linii.

W obu przypadkach powinieneś przynajmniej zobaczyć jedną pustą linię przed zablokowaniem, jak w pierwszym przykładzie wysłanym przez pierwszy numer printf(\n...).

0

Twój konkretny przykład nie wymaga interakcji "w czasie rzeczywistym". Następujące prace:

from subprocess import Popen, PIPE 

p = Popen(["./a.out"], stdin=PIPE, stdout=PIPE) 
output = p.communicate(b"12345")[0] # send input/read all output 
print output, 

gdzie a.out to Twój przykładowy program C.

W ogóle, do interakcji opartych dialogowym z podproces można użyć pexpect moduł (lub jego analogów na Windows):

import pexpect 

child = pexpect.spawn("./a.out") 
child.expect("input>>") 
child.sendline("12345.67890") # send a number 
child.expect(r"\d+\.\d+") # expect the number at the end 
print float(child.after) # assert that we can parse it 
child.close() 
-1

miałem ten sam problem, a "proc.communicate()" nie rozwiązuje go, ponieważ czeka na zakończenie procesu.

Więc tutaj jest to, co działa dla mnie, na Okna z Python 3.5.1:

import subprocess as sp 
myProcess = sp.Popen(cmd, creationflags=sp.CREATE_NEW_PROCESS_GROUP,stdout=sp.PIPE,stderr=sp.STDOUT) 
while i<40: 
     i+=1 
     time.sleep(.5) 
     out = myProcess.stdout.readline().decode("utf-8").rstrip() 

Chyba creationflags i inne argumenty nie są obowiązkowe (ale nie mam czasu na test), więc byłaby to minimalna składnia: