Żadna z odpowiedzi tutaj nie zaspokoiła wszystkich moich potrzeb.
- Brak wątków dla standardowego wyjścia (bez kolejek itp.)
- Nie blokuje, ponieważ muszę sprawdzić, czy dzieją się inne rzeczy
- Użyj PIPE, ponieważ potrzebowałem zrobić wiele rzeczy, np. Strumień wyjściowy, zapisz do pliku dziennika i zwróć kopię ciągu danych wyjściowych.
Małe tło: używam ThreadPoolExecutor do zarządzania pulą wątków, z których każdy uruchamia podproces i uruchamia je jednocześnie. (W Python2.7, ale powinno to również działać w nowszych wersjach 3.x). Nie chcę używać wątków tylko do zbierania danych wyjściowych, ponieważ chcę jak najwięcej dostępnych dla innych rzeczy (pula 20 procesów użyłaby 40 wątków tylko do uruchomienia; 1 dla wątku procesu i 1 dla standardowego wyjścia ... i więcej, jeśli chcesz stderr, tak myślę)
Usuwam tutaj wiele wyjątków i takich, więc jest to oparte na kodzie, który działa w środowisku produkcyjnym. Mam nadzieję, że nie zrujnowałem go w kopii i wkleiłem. Również opinie bardzo mile widziane!
import time
import fcntl
import subprocess
import time
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
# Make stdout non-blocking when using read/readline
proc_stdout = proc.stdout
fl = fcntl.fcntl(proc_stdout, fcntl.F_GETFL)
fcntl.fcntl(proc_stdout, fcntl.F_SETFL, fl | os.O_NONBLOCK)
def handle_stdout(proc_stream, my_buffer, echo_streams=True, log_file=None):
"""A little inline function to handle the stdout business. """
# fcntl makes readline non-blocking so it raises an IOError when empty
try:
for s in iter(proc_stream.readline, ''): # replace '' with b'' for Python 3
my_buffer.append(s)
if echo_streams:
sys.stdout.write(s)
if log_file:
log_file.write(s)
except IOError:
pass
# The main loop while subprocess is running
stdout_parts = []
while proc.poll() is None:
handle_stdout(proc_stdout, stdout_parts)
# ...Check for other things here...
# For example, check a multiprocessor.Value('b') to proc.kill()
time.sleep(0.01)
# Not sure if this is needed, but run it again just to be sure we got it all?
handle_stdout(proc_stdout, stdout_parts)
stdout_str = "".join(stdout_parts) # Just to demo
Jestem pewien, że dodano tutaj koszty ogólne, ale w moim przypadku nie stanowi to problemu. Funkcjonalnie robi to, czego potrzebuję. Jedyną rzeczą, której nie rozwiązałem, jest to, dlaczego działa to idealnie w przypadku komunikatów w dzienniku, ale widzę, że niektóre print
wiadomości pojawiają się później i jednocześnie.