odczyt podprocesu stdout linia po linii
Mój skrypt w Pythonie używa podprocesu, aby wywołać narzędzie linuksowe, które jest bardzo hałaśliwe. Chcę zapisać wszystkie dane wyjściowe do pliku dziennika i pokazać część z nich użytkownikowi. Myślałem, że to zadziała, ale wyjście nie pojawia się w mojej aplikacji, dopóki narzędzie nie wytworzy znaczącej 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()
Zachowanie, którego naprawdę chcę, polega na tym, aby skrypt filtra drukował każdą linię, gdy jest ona odbierana z podprocesu. Jakby co tee
robi ale z Pythonem kod.
Update:
Jeśli dodaje się sys.stdout.flush()
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życie proc.stdout.xreadlines()
działa tak samo jak py3k, ale tak nie jest.
Aktualizacja 2:
Oto minimalny kod roboczy.
#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()
6 answers
Minęło sporo czasu, odkąd ostatnio pracowałem z Pythonem, ale myślę, że problem jest z instrukcją for line in proc.stdout
, która odczytuje całe dane wejściowe przed iteracją. Rozwiązaniem jest użycie readline()
zamiast:
#filters output
import subprocess
proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE)
while True:
line = proc.stdout.readline()
if line != '':
#the real code does filtering here
print "test:", line.rstrip()
else:
break
Oczywiście nadal musisz poradzić sobie z buforowaniem podprocesu.
Uwaga: zgodnie z dokumentacją Rozwiązanie z iteratorem powinno być równoważne z użyciem readline()
, z wyjątkiem bufora do odczytu, ale (Lub właśnie z tego powodu) proponowane zmiana dała mi Inne wyniki (Python 2.5 NA Windows XP).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-11 18:56:24
Trochę za późno na imprezę, ale zdziwiło mnie, że nie widzę tu najprostszego rozwiązania:
import io
import subprocess
proc = subprocess.Popen(["prog", "arg"], stdout=subprocess.PIPE)
for line in io.TextIOWrapper(proc.stdout, encoding="utf-8"): # or another encoding
# do something with line
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-22 03:56:27
Rzeczywiście, jeśli rozwiązałeś iterator, buforowanie może być teraz Twoim problemem. Możesz powiedzieć pythonowi w podprocesie, aby nie buforował swoich danych wyjściowych.
proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE)
Staje się
proc = subprocess.Popen(['python','-u', 'fake_utility.py'],stdout=subprocess.PIPE)
Potrzebowałem tego podczas wywoływania Pythona z poziomu Pythona.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-29 16:36:03
Chcesz przekazać te dodatkowe parametry subprocess.Popen
:
bufsize=1, universal_newlines=True
Następnie możesz iterować jak w twoim przykładzie. (Testowane z Pythonem 3.5)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-11 18:11:23
Następująca modyfikacja odpowiedzi Rômulo działa dla mnie na Pythonie 2 i 3 (2.7.12 i 3.6.1):
import os
import subprocess
process = subprocess.Popen(command, stdout=subprocess.PIPE)
while True:
line = process.stdout.readline()
if line != b'':
os.write(1, line)
else:
break
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-02 17:14:13
Próbowałem tego z python3 i zadziałało, source
def output_reader(proc):
for line in iter(proc.stdout.readline, b''):
print('got line: {0}'.format(line.decode('utf-8')), end='')
def main():
proc = subprocess.Popen(['python', 'fake_utility.py'],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
t = threading.Thread(target=output_reader, args=(proc,))
t.start()
try:
time.sleep(0.2)
import time
i = 0
while True:
print (hex(i)*512)
i += 1
time.sleep(0.5)
finally:
proc.terminate()
try:
proc.wait(timeout=0.2)
print('== subprocess exited with rc =', proc.returncode)
except subprocess.TimeoutExpired:
print('subprocess did not terminate in time')
t.join()
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-21 12:00:42