Aby uruchomić programy z moich skryptów Python, używam następującej metody:
def execute(command):
process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
output = process.communicate()[0]
exitCode = process.returncode
if (exitCode == 0):
return output
else:
raise ProcessException(command, exitCode, output)
Więc kiedy uruchamiam taki proces Process.execute("mvn clean install")
, mój program czeka, aż proces się zakończy, i dopiero wtedy otrzymuję pełne wyjście mojego programu. Jest to denerwujące, jeśli uruchamiam proces, który zajmuje trochę czasu.
Czy mogę pozwolić mojemu programowi zapisywać dane wyjściowe procesu wiersz po wierszu, odpytując dane wyjściowe procesu, zanim zakończy ono pętlę lub coś takiego?
** [EDYCJA] Przepraszam, nie szukałem zbyt dobrze przed opublikowaniem tego pytania. Wątek jest właściwie kluczem. Znaleziono tutaj przykład, który pokazuje, jak to zrobić: ** Python Subprocess.Popen z wątku
python
subprocess
Ingo Fischer
źródło
źródło
Odpowiedzi:
Można użyć ITER przetwarzać linie jak najszybciej wyjść sterujących nich
lines = iter(fd.readline, "")
. Oto pełny przykład pokazujący typowy przypadek użycia (podziękowania dla @jfs za pomoc):źródło
for line in popen.stdout: print(line.decode(), end='')
. Aby obsługiwać zarówno Python 2, jak i 3, używaj bajtów dosłownych: wb''
przeciwnym razielines_iterator
nigdy nie kończy się na Python 3.bufsize=1
(może to poprawić wydajność w Pythonie 2), zamknąćpopen.stdout
potok jawnie (bez czekania, aż zajmie się tym śmieci) i podnieśćsubprocess.CalledProcessError
(jakcheck_call()
,check_output()
zrób).print
Stwierdzenie jest inna w Pythonie 2 i 3: można użyć hack Softspaceprint line,
(uwaga: przecinek), aby uniknąć podwajania wszystkie znaki nowej linii jak kodzie robi i przechodzącuniversal_newlines=True
na Python 3, aby uzyskać tekst zamiast bytes- powiązanej odpowiedź .execute(["python", "-u", "child_thread.py"])
. Więcej informacji: stackoverflow.com/questions/14258500/…Ok, udało mi się go rozwiązać bez wątków (doceniamy wszelkie sugestie, dlaczego użycie wątków byłoby lepsze) za pomocą fragmentu tego pytania Przechwytywanie standardowego przebiegu podprocesu podczas jego działania
źródło
print line,
nasys.stdout.write(nextline); sys.stdout.flush()
. W przeciwnym razie drukowałby co dwa wiersze. Z drugiej strony używa to interfejsu Notatnika IPython, więc może działo się coś innego - niezależnie od jawnego wywołaniaflush()
działa.stderr=subprocess.STDOUT
sięstderr=subprocess.PIPE
, a następnie zadzwonićprocess.stderr.readline()
od wewnątrz pętli, wydaje mi się uruchomić w konflikt z bardzo impasu, który ostrzegał w dokumentacji dosubprocess
modułu.stdout=subprocess.PIPE,stderr=subprocess.STDOUT
to przechwytywanie stderr i wierzę (ale nie testowałem), że przechwytuje także stdin.Aby wydrukować wynik podprocesu wiersz po wierszu, jak tylko bufor stdout zostanie opróżniony w Pythonie 3:
Uwaga: nie potrzebujesz
p.poll()
- pętla kończy się po osiągnięciu eof. I nie potrzebujesziter(p.stdout.readline, '')
- błąd odczytu z wyprzedzeniem został naprawiony w Pythonie 3.Zobacz także Python: odczyt danych strumieniowych z subprocess.communicate () .
źródło
sys.stdout.flush()
w obiekcie nadrzędnym - stdout jest buforowany w linii, jeśli nie jest przekierowywany do pliku / potoku, a zatem drukowanieline
automatycznie opróżnia bufor. Nie potrzebujesz teżsys.stdout.flush()
w dziecku --u
zamiast tego podaj opcję wiersza poleceń.>
uruchompython -u your-script.py > some-file
. Uwaga:-u
opcja, o której wspomniałem powyżej (nie trzeba jej używaćsys.stdout.flush()
).p.wait()
- jest wywoływany przy wyjściu zwith
bloku. Zastosowaniep.returncode
.Jest naprawdę bardzo prosty sposób, aby to zrobić, gdy chcesz po prostu wydrukować wynik:
Tutaj po prostu wskazujemy podproces na nasze standardowe wyjście i wykorzystujemy istniejące API powodzenia lub wyjątku.
źródło
shell=True
@tokland
wypróbowałem kod i poprawiłem go dla 3.4, a Windows dir.cmd to proste polecenie dir, zapisane jako plik cmd
źródło
iter()
iend='\r\n'
są niepotrzebne. Python domyślnie używa uniwersalnego trybu nowego wiersza, tzn. Każdy'\n'
jest tłumaczony'\r\n'
podczas drukowania.'latin'
jest prawdopodobnie nieprawidłowym kodowaniem, możesz użyć gouniversal_newlines=True
do uzyskania tekstu w Pythonie 3 (dekodowanego przy użyciu preferowanego kodowania regionalnego). Nie przestawaj.poll()
, mogą być buforowane nieprzeczytane dane. Jeśli skrypt Pythona jest uruchomiony w konsoli, wówczas jego dane wyjściowe są buforowane w linii; możesz wymusić buforowanie linii za pomocą-u
opcji - nie potrzebujeszflush=True
tutaj.Jeśli ktoś chce czytać z obu
stdout
istderr
jednocześnie używać wątków, oto co wymyśliłem:Po prostu chciałem się tym podzielić, ponieważ skończyłem na tym pytaniu, próbując zrobić coś podobnego, ale żadna z odpowiedzi nie rozwiązała mojego problemu. Mam nadzieję, że to komuś pomaga!
Pamiętaj, że w moim przypadku proces zewnętrzny zabija proces, który my
Popen()
.źródło
Dla każdego, kto próbuje odpowiedzieć na to pytanie, aby uzyskać stdout ze skryptu Python, zauważ, że Python buforuje swoje standardowe wyjście, dlatego może minąć trochę czasu, zanim zobaczysz standardowe wyjście.
Można to naprawić, dodając następujące elementy po każdym zapisie standardowym w skrypcie docelowym:
źródło
import
drugi skrypt; zajrzyj domultiprocessing
lubthreading
jeśli potrzebujesz wykonania równoległego.subprocess.run("/path/to/python/executable", "pythonProgramToRun.py")
W Pythonie> = 3.5 używanie
subprocess.run
działa dla mnie:(pobieranie danych wyjściowych podczas wykonywania również działa bez
shell=True
) https://docs.python.org/3/library/subprocess.html#subprocess.runźródło
subprocess.run()
Wywołanie zwraca tylko wtedy, gdy podproces zakończy działanie.>>> import subprocess; subprocess.run('top')
wydaje się również drukować „podczas wykonywania” (a top nigdy się nie kończy). Może nie rozumiem subtelnej różnicy?stdout=subprocess.PIPE
Możesz go odczytać dopiero potop
zakończeniu. Twój program Python jest blokowany podczas wykonywania podprocesu.run
Metoda wciąż działa, jeśli jesteś zainteresowany tylko widząc wyjścia, jak to jest generowane. Jeśli chcesz zrobić coś z wyjściem w Pythonie asynchronicznie, masz rację, że to nie działa.Aby odpowiedzieć na pierwotne pytanie, najlepszym sposobem IMO jest po prostu przekierowanie podprocesu
stdout
bezpośrednio do twojego programustdout
(opcjonalnie to samo można zrobić dlastderr
, jak w przykładzie poniżej)źródło
stdout
istderr
robi to samo z mniejszym kodem. Chociaż przypuszczam, że wyraźne jest lepsze niż dorozumiane.Ten PoC stale odczytuje dane wyjściowe z procesu i można uzyskać do nich dostęp w razie potrzeby. Zachowywany jest tylko ostatni wynik, wszystkie inne dane wyjściowe są odrzucane, co zapobiega wzrostowi pamięci PIPE:
print_date.py
Dane wyjściowe: Możesz wyraźnie zobaczyć, że dane wyjściowe pochodzą tylko z ~ 2,5 s odstępu między nimi.
źródło
Działa to przynajmniej w Python3.4
źródło
Żadna z odpowiedzi tutaj nie zaspokoiła wszystkich moich potrzeb.
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!
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.źródło
W Pythonie 3.6 użyłem tego:
źródło
subprocess.call()
ma pewne brodawki, które są naprawiane przez nowsze funkcje; w Pythonie 3.6 zwykle byśsubprocess.run()
do tego użył ; dla wygody starsza funkcja opakowaniasubprocess.check_output()
jest również nadal dostępna - zwraca rzeczywiste dane wyjściowe z procesu (ten kod zwróci tylko kod wyjścia, ale nawet wtedy wydrukuje coś niezdefiniowanego).