Ciągłe drukowanie danych Podprocesowych podczas pracy procesu
Aby uruchomić programy z moich skryptów Pythona, 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 jak Process.execute("mvn clean install")
, Mój program czeka aż proces zostanie zakończony i dopiero wtedy otrzymuję pełny wynik mojego programu. Jest to denerwujące, jeśli prowadzę proces, który trwa chwilę, aby zakończyć.
Czy mogę pozwolić mojemu programowi napisać wyjście procesu linia po linii, przez przepytanie wyjścia procesu, zanim zakończy się w pętli lub coś w tym stylu?
* * [edytuj] Przepraszam, że nie. wyszukaj bardzo dobrze przed wysłaniem tego pytania. Gwintowanie jest w rzeczywistości kluczem. Znalazłem tutaj przykład, który pokazuje, jak to zrobić: ** Podproces Pythona.Popen z wątku
9 answers
Możesz użyć iter do przetwarzania linii, gdy tylko polecenie je wyświetli: lines = iter(fd.readline, "")
. Oto pełny przykład pokazujący typowy przypadek użycia (dzięki @jfs za pomoc):
from __future__ import print_function # Only Python 2.x
import subprocess
def execute(cmd):
popen = subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True)
for stdout_line in iter(popen.stdout.readline, ""):
yield stdout_line
popen.stdout.close()
return_code = popen.wait()
if return_code:
raise subprocess.CalledProcessError(return_code, cmd)
# Example
for path in execute(["locate", "a"]):
print(path, end="")
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-07-27 22:46:13
Ok udało mi się rozwiązać go bez wątków (wszelkie sugestie, dlaczego używanie wątków byłoby lepsze, są mile widziane), używając fragmentu z tego pytania przechwytującego stdout podprocesu podczas jego działania
def execute(command):
process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
# Poll process for new output until finished
while True:
nextline = process.stdout.readline()
if nextline == '' and process.poll() is not None:
break
sys.stdout.write(nextline)
sys.stdout.flush()
output = process.communicate()[0]
exitCode = process.returncode
if (exitCode == 0):
return output
else:
raise ProcessException(command, exitCode, output)
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-05-23 12:02:22
Aby wydrukować wyjście podprocesu linia po linii, gdy tylko jego bufor stdout zostanie spłukany w Pythonie 3:
from subprocess import Popen, PIPE, CalledProcessError
with Popen(cmd, stdout=PIPE, bufsize=1, universal_newlines=True) as p:
for line in p.stdout:
print(line, end='') # process line here
if p.returncode != 0:
raise CalledProcessError(p.returncode, p.args)
Uwaga: nie potrzebujesz p.poll()
-- pętla kończy się po osiągnięciu eof. I nie potrzebujesz iter(p.stdout.readline, '')
-- Błąd odczytu z wyprzedzeniem został naprawiony w Pythonie 3.
Zobacz także, Python: odczyt strumieniowego wejścia z podprocesu.communicate () .
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-05-23 11:47:12
@tokland
Wypróbowałem Twój kod i poprawiłem go dla 3.4 i windows reż.cmd jest prostym poleceniem dir, zapisanym jako cmd-file
import subprocess
c = "dir.cmd"
def execute(command):
popen = subprocess.Popen(command, stdout=subprocess.PIPE,bufsize=1)
lines_iterator = iter(popen.stdout.readline, b"")
while popen.poll() is None:
for line in lines_iterator:
nline = line.rstrip()
print(nline.decode("latin"), end = "\r\n",flush =True) # yield line
execute(c)
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-03-25 22:31:32
Dla każdego, kto próbuje odpowiedzi na to pytanie, aby uzyskać stdout ze skryptu Pythona, zauważ, że Python buforuje swoje stdout, a zatem może zająć trochę czasu, aby zobaczyć stdout.
Można to naprawić dodając po każdym zapisie stdout w skrypcie docelowym:
sys.stdout.flush()
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-06-21 12:40:23
Ten PoC stale odczytuje dane wyjściowe z procesu i może być dostępny w razie potrzeby. Tylko ostatni wynik jest przechowywany, wszystkie inne wyjście jest odrzucane, dlatego zapobiega wyrastaniu rury z pamięci:
import subprocess
import time
import threading
import Queue
class FlushPipe(object):
def __init__(self):
self.command = ['python', './print_date.py']
self.process = None
self.process_output = Queue.LifoQueue(0)
self.capture_output = threading.Thread(target=self.output_reader)
def output_reader(self):
for line in iter(self.process.stdout.readline, b''):
self.process_output.put_nowait(line)
def start_process(self):
self.process = subprocess.Popen(self.command,
stdout=subprocess.PIPE)
self.capture_output.start()
def get_output_for_processing(self):
line = self.process_output.get()
print ">>>" + line
if __name__ == "__main__":
flush_pipe = FlushPipe()
flush_pipe.start_process()
now = time.time()
while time.time() - now < 10:
flush_pipe.get_output_for_processing()
time.sleep(2.5)
flush_pipe.capture_output.join(timeout=0.001)
flush_pipe.process.kill()
Print_date.py
#!/usr/bin/env python
import time
if __name__ == "__main__":
while True:
print str(time.time())
time.sleep(0.01)
Output: wyraźnie widać, że jest tylko wyjście z ~2,5 s odstępu, pomiędzy którym nic nie ma.
>>>1520535158.51
>>>1520535161.01
>>>1520535163.51
>>>1520535166.01
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-03-08 20:02:28
W Pythonie > = 3.5 używanie subprocess.run
działa dla mnie:
import subprocess
cmd = 'echo foo; sleep 1; echo foo; sleep 2; echo foo'
subprocess.run(cmd, shell=True)
(uzyskanie wyniku podczas wykonywania pracy działa również bez shell=True
)
https://docs.python.org/3/library/subprocess.html#subprocess.run
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-10-05 06:56:27
Jeśli ktoś chce czytać zarówno z stdout
jak i stderr
w tym samym czasie używając wątków, oto co wymyśliłem:
import threading
import subprocess
import Queue
class AsyncLineReader(threading.Thread):
def __init__(self, fd, outputQueue):
threading.Thread.__init__(self)
assert isinstance(outputQueue, Queue.Queue)
assert callable(fd.readline)
self.fd = fd
self.outputQueue = outputQueue
def run(self):
map(self.outputQueue.put, iter(self.fd.readline, ''))
def eof(self):
return not self.is_alive() and self.outputQueue.empty()
@classmethod
def getForFd(cls, fd, start=True):
queue = Queue.Queue()
reader = cls(fd, queue)
if start:
reader.start()
return reader, queue
process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(stdoutReader, stdoutQueue) = AsyncLineReader.getForFd(process.stdout)
(stderrReader, stderrQueue) = AsyncLineReader.getForFd(process.stderr)
# Keep checking queues until there is no more output.
while not stdoutReader.eof() or not stderrReader.eof():
# Process all available lines from the stdout Queue.
while not stdoutQueue.empty():
line = stdoutQueue.get()
print 'Received stdout: ' + repr(line)
# Do stuff with stdout line.
# Process all available lines from the stderr Queue.
while not stderrQueue.empty():
line = stderrQueue.get()
print 'Received stderr: ' + repr(line)
# Do stuff with stderr line.
# Sleep for a short time to avoid excessive CPU use while waiting for data.
sleep(0.05)
print "Waiting for async readers to finish..."
stdoutReader.join()
stderrReader.join()
# Close subprocess' file descriptors.
process.stdout.close()
process.stderr.close()
print "Waiting for process to exit..."
returnCode = process.wait()
if returnCode != 0:
raise subprocess.CalledProcessError(returnCode, command)
Po prostu chciałem się tym podzielić, ponieważ skończyło się na tym pytaniu, próbując zrobić coś podobnego, ale żadna z odpowiedzi nie rozwiązała mojego problemu. Mam nadzieję, że to komuś pomoże!
Zauważ, że w moim przypadku, zewnętrzny proces zabija proces, który my Popen()
.
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-07-11 00:36:14
To działa przynajmniej w Python3. 4
import subprocess
process = subprocess.Popen(cmd_list, stdout=subprocess.PIPE)
for line in process.stdout:
print(line.decode().strip())
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-07-20 01:36:58