Ich suche nach einer Python-Lösung, mit der ich die Ausgabe eines Befehls in einer Datei speichern kann, ohne sie vor der Konsole zu verbergen.
Zu Ihrer Information : Ich frage nach tee (als Unix-Befehlszeilenprogramm) und nicht nach der gleichnamigen Funktion aus dem Python-Intertools-Modul.
Einzelheiten
- Python-Lösung (nicht aufrufend
tee
, unter Windows nicht verfügbar) - Ich muss keine Eingabe für stdin für den aufgerufenen Prozess bereitstellen
- Ich habe keine Kontrolle über das aufgerufene Programm. Ich weiß nur, dass es etwas an stdout und stderr ausgibt und mit einem Exit-Code zurückgibt.
- Zum Aufrufen externer Programme (Unterprozess)
- Für beide arbeiten
stderr
undstdout
- In der Lage zu sein, zwischen stdout und stderr zu unterscheiden, weil ich möglicherweise nur eines davon auf der Konsole anzeigen möchte oder versuchen könnte, stderr mit einer anderen Farbe auszugeben - dies bedeutet, dass dies
stderr = subprocess.STDOUT
nicht funktioniert. - Live-Ausgabe (progressiv) - Der Prozess kann lange dauern und ich kann es kaum erwarten, bis er abgeschlossen ist.
- Python 3-kompatibler Code (wichtig)
Verweise
Hier sind einige unvollständige Lösungen, die ich bisher gefunden habe:
- http://devlishgenius.blogspot.com/2008/10/logging-in-real-time-in-python.html (mkfifo funktioniert nur unter Unix)
- http://blog.kagesenshi.org/2008/02/teeing-python-subprocesspopen-output.html (funktioniert überhaupt nicht)
Diagramm http://blog.i18n.ro/wp-content/uploads/2010/06/Drawing_tee_py.png
Aktueller Code (zweiter Versuch)
#!/usr/bin/python
from __future__ import print_function
import sys, os, time, subprocess, io, threading
cmd = "python -E test_output.py"
from threading import Thread
class StreamThread ( Thread ):
def __init__(self, buffer):
Thread.__init__(self)
self.buffer = buffer
def run ( self ):
while 1:
line = self.buffer.readline()
print(line,end="")
sys.stdout.flush()
if line == '':
break
proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdoutThread = StreamThread(io.TextIOWrapper(proc.stdout))
stderrThread = StreamThread(io.TextIOWrapper(proc.stderr))
stdoutThread.start()
stderrThread.start()
proc.communicate()
stdoutThread.join()
stderrThread.join()
print("--done--")
#### test_output.py ####
#!/usr/bin/python
from __future__ import print_function
import sys, os, time
for i in range(0, 10):
if i%2:
print("stderr %s" % i, file=sys.stderr)
else:
print("stdout %s" % i, file=sys.stdout)
time.sleep(0.1)
Echte Ausgabe
stderr 1
stdout 0
stderr 3
stdout 2
stderr 5
stdout 4
stderr 7
stdout 6
stderr 9
stdout 8
--done--
Bei der erwarteten Ausgabe sollten die Zeilen bestellt werden. Bemerkung, das Ändern des Popen, um nur ein PIPE zu verwenden, ist nicht erlaubt, da ich im wirklichen Leben verschiedene Dinge mit stderr und stdout machen möchte.
Auch im zweiten Fall war ich nicht in der Lage, Echtzeit-Outs zu erhalten. Tatsächlich gingen alle Ergebnisse ein, als der Prozess abgeschlossen war. Standardmäßig sollte Popen keine Puffer verwenden (bufsize = 0).
Antworten:
Ich sehe, dass dies ein ziemlich alter Beitrag ist, aber nur für den Fall, dass noch jemand nach einer Möglichkeit sucht, dies zu tun:
proc = subprocess.Popen(["ping", "localhost"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) with open("logfile.txt", "w") as log_file: while proc.poll() is None: line = proc.stderr.readline() if line: print "err: " + line.strip() log_file.write(line) line = proc.stdout.readline() if line: print "out: " + line.strip() log_file.write(line)
quelle
stdout, stderr = proc.communicate()
einfacher zu bedienen fand.readline()
mitreadline(size)
. Ich habe etwas Ähnliches in anderen Sprachen gemacht. Ref: docs.python.org/3/library/io.html#io.TextIOBase.readlinereadline(size)
wird den Deadlock nicht beheben. stdout / stderr sollte gleichzeitig gelesen werden. Siehe Links unter der Frage, die Lösungen mit Threads oder Asyncio anzeigen.Dies ist eine einfache Portierung von
tee
Python.import sys sinks = sys.argv[1:] sinks = [open(sink, "w") for sink in sinks] sinks.append(sys.stderr) while True: input = sys.stdin.read(1024) if input: for sink in sinks: sink.write(input) else: break
Ich laufe gerade unter Linux, aber das sollte auf den meisten Plattformen funktionieren.
Nun zum
subprocess
Teil, weiß ich nicht , wie Sie mit ‚Draht‘ wollen die subprocess desstdin
,stdout
undstderr
zu Ihrerstdin
,stdout
,stderr
und Datei sinkt, aber ich weiß, dies zu tun:import subprocess callee = subprocess.Popen( ["python", "-i"], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE )
Jetzt können Sie zugreifen
callee.stdin
,callee.stdout
undcallee.stderr
wie normale Dateien, so dass die obige „Lösung“ zu arbeiten. Wenn Sie das erhalten möchtencallee.returncode
, müssen Sie einen zusätzlichen Anruf tätigencallee.poll()
.Seien Sie vorsichtig beim Schreiben an
callee.stdin
: Wenn der Prozess dabei beendet wurde, kann ein Fehler auftreten (unter Linux verstehe ichIOError: [Errno 32] Broken pipe
).quelle
tee(f_in, f_out, len, flags)
API bereitstellt, aber das ist nicht der Punkt, oder?stdout
, man liest weiterstderr
. Wenn Sie beide in dieselbe Datei schreiben möchten, können Sie eine Sperre für die Senken erwerben, wenn Sie mit dem Lesen beginnen, und diese nach dem Schreiben eines Zeilenabschlusses freigeben. : /So kann es gemacht werden
import sys from subprocess import Popen, PIPE with open('log.log', 'w') as log: proc = Popen(["ping", "google.com"], stdout=PIPE, encoding='utf-8') while proc.poll() is None: text = proc.stdout.readline() log.write(text) sys.stdout.write(text)
quelle
print()
anstelle von verwendensys.stdout.write()
. :-)print
fügt eine zusätzliche neue Zeile hinzu, die nicht Ihren Wünschen entspricht, wenn Sie die Ausgabe originalgetreu wiedergeben möchten.print(line, end='')
könnte das Problem lösenWenn das Erfordernis von Python 3.6 kein Problem darstellt, gibt es jetzt eine Möglichkeit, dies mit Asyncio zu tun. Mit dieser Methode können Sie stdout und stderr getrennt erfassen, aber beide Streams ohne Verwendung von Threads zum tty übertragen. Hier ist eine grobe Übersicht:
class RunOutput(): def __init__(self, returncode, stdout, stderr): self.returncode = returncode self.stdout = stdout self.stderr = stderr async def _read_stream(stream, callback): while True: line = await stream.readline() if line: callback(line) else: break async def _stream_subprocess(cmd, stdin=None, quiet=False, echo=False) -> RunOutput: if isWindows(): platform_settings = {'env': os.environ} else: platform_settings = {'executable': '/bin/bash'} if echo: print(cmd) p = await asyncio.create_subprocess_shell(cmd, stdin=stdin, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, **platform_settings) out = [] err = [] def tee(line, sink, pipe, label=""): line = line.decode('utf-8').rstrip() sink.append(line) if not quiet: print(label, line, file=pipe) await asyncio.wait([ _read_stream(p.stdout, lambda l: tee(l, out, sys.stdout)), _read_stream(p.stderr, lambda l: tee(l, err, sys.stderr, label="ERR:")), ]) return RunOutput(await p.wait(), out, err) def run(cmd, stdin=None, quiet=False, echo=False) -> RunOutput: loop = asyncio.get_event_loop() result = loop.run_until_complete( _stream_subprocess(cmd, stdin=stdin, quiet=quiet, echo=echo) ) return result
Der obige Code basiert auf diesem Blog-Beitrag: https://kevinmccarthy.org/2016/07/25/streaming-subprocess-stdin-and-stdout-with-asyncio-in-python/
quelle
Wenn Sie nicht mit dem Prozess interagieren möchten, können Sie das Unterprozessmodul problemlos verwenden.
Beispiel:
tester.py
import os import sys for file in os.listdir('.'): print file sys.stderr.write("Oh noes, a shrubbery!") sys.stderr.flush() sys.stderr.close()
testing.py
import subprocess p = subprocess.Popen(['python', 'tester.py'], stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() print stdout, stderr
In Ihrer Situation können Sie einfach zuerst stdout / stderr in eine Datei schreiben. Sie können Argumente auch mit communic an Ihren Prozess senden, obwohl ich nicht herausfinden konnte, wie ich kontinuierlich mit dem Unterprozess interagieren kann.
quelle
p.poll()
diese einfach, um sie abzurufen.Versuche dies :
import sys class tee-function : def __init__(self, _var1, _var2) : self.var1 = _var1 self.var2 = _var2 def __del__(self) : if self.var1 != sys.stdout and self.var1 != sys.stderr : self.var1.close() if self.var2 != sys.stdout and self.var2 != sys.stderr : self.var2.close() def write(self, text) : self.var1.write(text) self.var2.write(text) def flush(self) : self.var1.flush() self.var2.flush() stderrsav = sys.stderr out = open(log, "w") sys.stderr = tee-function(stderrsav, out)
quelle
closed
.subprocess.Popen
rufe anfileno()
und löse eine Ausnahme aus.Meine Lösung ist nicht elegant, aber sie funktioniert.
Sie können Powershell verwenden, um unter WinOS Zugriff auf "Tee" zu erhalten.
import subprocess import sys cmd = ['powershell', 'ping', 'google.com', '|', 'tee', '-a', 'log.txt'] if 'darwin' in sys.platform: cmd.remove('powershell') p = subprocess.Popen(cmd) p.wait()
quelle
ping
MacOS aus.