Mój skrypt Pythona używa podprocesu do wywołania bardzo głośnego narzędzia linux. Chcę zapisać wszystkie dane wyjściowe w pliku dziennika i pokazać niektóre z nich użytkownikowi. Myślałem, że następujące działania będą działać, ale dane wyjściowe nie są wyświetlane w mojej aplikacji, dopóki narzędzie nie wygeneruje znacznej ilości danych wyjściowych.
#fake_utility.py, just generates lots of output over time
import time
i = 0
while True:
print hex(i)*512
i += 1
time.sleep(0.5)
#filters output
import subprocess
proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE)
for line in proc.stdout:
#the real code does filtering here
print "test:", line.rstrip()
Naprawdę chcę, aby skrypt filtrujący wypisał każdą linię otrzymaną z podprocesu. Trochę jak to, co tee
robi, ale z kodem python.
czego mi brakuje? Czy to w ogóle możliwe?
Aktualizacja:
Jeśli a sys.stdout.flush()
zostanie dodane do fake_utility.py, kod ma pożądane zachowanie w Pythonie 3.1. Używam Pythona 2.6. Można by pomyśleć, że używanie proc.stdout.xreadlines()
działałoby tak samo jak py3k, ale tak nie jest.
Aktualizacja 2:
Oto minimalny działający kod.
#fake_utility.py, just generates lots of output over time
import sys, time
for i in range(10):
print i
sys.stdout.flush()
time.sleep(0.5)
#display out put line by line
import subprocess
proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE)
#works in python 3.0+
#for line in proc.stdout:
for line in iter(proc.stdout.readline,''):
print line.rstrip()
źródło
print line,
zamiastprint line.rstrip()
(uwaga: przecinek na końcu).subprocess.communicate()
Odpowiedzi:
Minęło dużo czasu, odkąd ostatnio pracowałem z Pythonem, ale myślę, że problem dotyczy instrukcji
for line in proc.stdout
, która odczytuje cały wkład przed iteracją. Rozwiązaniem jest użyciereadline()
zamiast tego:Oczywiście nadal masz do czynienia z buforowaniem podprocesu.
Uwaga: zgodnie z dokumentacją rozwiązanie z iteratorem powinno być równoważne z używaniem
readline()
, z wyjątkiem bufora do odczytu z wyprzedzeniem, ale (lub właśnie z tego powodu) proponowana zmiana dała mi różne wyniki (Python 2.5 w systemie Windows XP).źródło
file.readline()
vs.for line in file
patrz bugs.python.org/issue3907 (w skrócie: działa na Python3; użyjio.open()
na Python 2.6+)for line in iter(proc.stdout.readline, ''):
.for line in proc.stdout
w Pythonie 3 (nie ma błędu odczytu z wyprzedzeniem) 2.'' != b''
w Pythonie 3 - nie kopiuj i nie wklejaj kodu na ślepo - pomyśl, co robi i jak działa.iter(f.readline, b'')
rozwiązanie jest dość oczywiste (i działa również na Pythonie 2, jeśli ktoś jest zainteresowany). Celem mojego komentarza nie było obwinianie twojego rozwiązania (przepraszam, jeśli tak wyglądało, ja też to teraz czytam!), Ale opisanie zakresu objawów, które w tym przypadku są dość poważne (większość Py2 / 3 problemy powodują wyjątki, podczas gdy dobrze zachowująca się pętla zmieniła się na nieskończoną, a odśmiecanie walczy z powodzią nowo tworzonych obiektów, powodując oscylacje wykorzystania pamięci o długim okresie i dużej amplitudzie).Trochę późno na imprezę, ale zdziwiło mnie, że nie widzę tutaj najprostszego rozwiązania:
(Wymaga to Python 3.)
źródło
AttributeError: 'file' object has no attribute 'readable'
py2.7TextIOWrapper
czy nie. Możesz po prostu obsłużyć wyjątek.Rzeczywiście, jeśli uporządkowałeś iterator, buforowanie może być teraz twoim problemem. Można powiedzieć pythonowi w podprocesie, aby nie buforował danych wyjściowych.
staje się
Potrzebowałem tego podczas wywoływania Pythona z poziomu Pythona.
źródło
Chcesz przekazać te dodatkowe parametry do
subprocess.Popen
:Następnie możesz iterować jak w twoim przykładzie. (Testowane z Python 3.5)
źródło
Funkcja, która pozwala na iterację po obu
stdout
istderr
jednocześnie, w czasie rzeczywistym, linia po liniiJeśli potrzebujesz uzyskać strumień wyjściowy dla obu
stdout
istderr
jednocześnie, możesz użyć następującej funkcji.Funkcja używa kolejek do łączenia obu potoków Popen w jeden iterator.
Tutaj tworzymy funkcję
read_popen_pipes()
:read_popen_pipes()
w użyciu:źródło
Możesz także odczytać linie bez pętli. Działa w python3.6.
źródło
list_of_strings = [x.decode('utf-8').rstrip('\n') for x in iter(process.stdout.readlines())]
Próbowałem tego z python3 i działało, źródło
źródło
Następująca modyfikacja odpowiedzi Rômulo działa dla mnie w Pythonie 2 i 3 (2.7.12 i 3.6.1):
źródło
Nie wiem, kiedy zostało to dodane do modułu podprocesu, ale w Pythonie 3 powinieneś używać
proc.stdout.splitlines()
:źródło