Wie leite ich stdout in eine beliebige Datei in Python um?
Wenn ein lang laufendes Python-Skript (z. B. eine Webanwendung) innerhalb der SSH-Sitzung gestartet und zurückgesetzt wird und die SSH-Sitzung geschlossen wird, löst die Anwendung IOError aus und schlägt fehl, sobald versucht wird, in stdout zu schreiben. Ich musste einen Weg finden, die Anwendung und die Module in eine Datei anstatt in stdout auszugeben, um einen Fehler aufgrund von IOError zu verhindern. Derzeit verwende ich nohup, um die Ausgabe in eine Datei umzuleiten, und das erledigt den Job, aber ich habe mich aus Neugier gefragt, ob es eine Möglichkeit gibt, dies ohne Verwendung von nohup zu tun.
Ich habe es bereits versucht sys.stdout = open('somefile', 'w')
, aber dies scheint nicht zu verhindern, dass einige externe Module weiterhin an das Terminal ausgegeben werden (oder die sys.stdout = ...
Leitung wurde möglicherweise überhaupt nicht ausgelöst). Ich weiß, dass es mit einfacheren Skripten funktionieren sollte, mit denen ich getestet habe, aber ich hatte auch noch keine Zeit, um eine Webanwendung zu testen.
script.p > file
someprocess | python script.py
? Warum einbeziehennohup
?print
Anweisungen neu, um daslogging
Modul aus der stdlib anzuwenden. Dann können Sie die Ausgabe überall umleiten, steuern, wie viel Ausgabe Sie möchten usw. In den meisten Fällen sollte Produktionscodeprint
aber nichtlog
.Antworten:
Wenn Sie die Umleitung innerhalb des Python-Skripts durchführen möchten, reicht die Einstellung
sys.stdout
auf ein Dateiobjekt aus:Eine weitaus häufigere Methode ist die Verwendung der Shell-Umleitung bei der Ausführung (unter Windows und Linux):
quelle
from sys import stdout
, vielleicht weil es eine lokale Kopie erstellt. Sie können es auch verwenden mitwith
zwith open('file', 'w') as sys.stdout: functionThatPrints()
. Sie können jetztfunctionThatPrints()
mit normalenprint
Anweisungen implementieren .stdout = sys.stdout
damit Sie sie zurücksetzen können, wenn Sie fertig sindsys.stdout = stdout
. Auf diese Weise vermasseln Sie sie nicht, wenn Sie von einer Funktion aufgerufen werden, die sie verwendetprint
.buffering=0
die Pufferung (dies kann die Leistung beeinträchtigen (10-100 Mal)).buffering=1
Aktiviert die Zeilenpufferung, sodass Sie sietail -f
für eine zeilenorientierte Ausgabe verwenden können.sys.stdout = sys.__stdout__
, um es zurück zu bekommen.In Python 3.4 gibt es eine
contextlib.redirect_stdout()
Funktion :Es ist ähnlich wie:
Dies kann in früheren Python-Versionen verwendet werden. Die letztere Version ist nicht wiederverwendbar . Es kann auf Wunsch auch hergestellt werden.
Das stdout wird auf der Ebene der Dateideskriptoren nicht umgeleitet, z.
b'not redirected'
und'echo this also is not redirected'
werden nicht in dieoutput.txt
Datei umgeleitet .Zum Umleiten auf Dateideskriptorebene kann Folgendes
os.dup2()
verwendet werden:Das gleiche Beispiel funktioniert jetzt, wenn
stdout_redirected()
anstelle vonredirect_stdout()
:Die Ausgabe, die zuvor auf stdout gedruckt wurde, wird jetzt angezeigt,
output.txt
solange derstdout_redirected()
Kontextmanager aktiv ist.Hinweis:
stdout.flush()
Leert keine C stdio-Puffer in Python 3, in denen E / A direkt inread()
/write()
system-Aufrufen implementiert ist . Um alle offenen C stdio-Ausgabestreams zu leeren, können Sielibc.fflush(None)
explizit aufrufen, wenn eine C-Erweiterung stdio-basierte E / A verwendet:Sie können
stdout
Parameter verwenden, um andere Streams umzuleiten, nicht nursys.stdout
zum Zusammenführensys.stderr
undsys.stdout
:Beispiel:
Hinweis:
stdout_redirected()
Mischt gepufferte E / A (sys.stdout
normalerweise) und ungepufferte E / A (Operationen an Dateideskriptoren direkt). Vorsicht, es könnte sein , Pufferung Probleme .Um zu antworten, Ihre Bearbeitung: Sie könnten verwenden,
python-daemon
um Ihr Skript zu dämonisieren undlogging
Modul (wie von @ erikb85 vorgeschlagen ) anstelle vonprint
Anweisungen zu verwenden und lediglich stdout für Ihr lang laufendes Python-Skript umzuleiten, das Sienohup
jetzt verwenden.quelle
stdout_redirected
ist hilfreich. Beachten Sie, dass dies in Doctests nicht funktioniert, da der spezielleSpoofOut
Handler, den Doctest zum Ersetzensys.stdout
verwendet, keinfileno
Attribut hat.ValueError("Expected a file (`.fileno()`) or a file descriptor")
ist es ein Fehler. Bist du sicher, dass es das nicht erhöht?doctest.sys.__stdout__
wo wir normalerweise verwenden würdensys.stdout
. Dies ist kein Problem mit Ihrer Funktion, sondern nur eine für doctest erforderliche Anpassung, da sie stdout durch ein Objekt ersetzt, das nicht alle Attribute aufweist, die eine echte Datei hätte.stdout_redirected()
Wenn Sie einenstdout
Parameter haben, können Sie ihn auf setzen,sys.__stdout__
wenn Sie das ursprüngliche Python-Standardout umleiten möchten (das sollte.fileno()
in den meisten Fällen gültig sein ). Es tut nichts für den Strom,sys.stdout
wenn sie unterschiedlich sind. Nicht benutzendoctest.sys
; es ist versehentlich verfügbar.with stdout_redirected(to=fd):
with merged_stderr_stdout():
print('...'); print('...', file=sys.stderr)
Sie können dies zu viel besser versuchen
quelle
logger
odersyslog
?def __getattr__(self, attr): return getattr(self.terminal, attr)
def flush(self):
der Klasse auch eine Methode hinzufügenLogger
.Die anderen Antworten betrafen nicht den Fall, in dem gegabelte Prozesse Ihr neues Standard freigeben sollen.
Das zu tun:
quelle
sys.stdout.flush()
vor derclose(1)
Anweisung ein ein, um sicherzustellen, dass die Umleitungsdatei'file'
die Ausgabe erhält. Sie können auch einetempfile.mkstemp()
Datei anstelle von verwenden'file'
. Und seien Sie vorsichtig, dass keine anderen Threads ausgeführt werden, die das erste Dateihandle des Betriebssystems nach dem,os.close(1)
aber vor dem'file'
Öffnen stehlen können, um das Handle zu verwenden.os.dup2()
und in einen Kontextmanager einbinden, wie in meiner Antwort gezeigtZitiert aus PEP 343 - Die "with" -Anweisung (hinzugefügte Importanweisung):
Stdout vorübergehend umleiten:
Wird wie folgt verwendet:
Dies ist natürlich nicht threadsicher, aber es wird auch nicht manuell getanzt. In Single-Thread-Programmen (zum Beispiel in Skripten) ist dies eine beliebte Methode, um Dinge zu tun.
quelle
os.system('echo not redirected')
. Meine Antwort zeigt, wie man eine solche Ausgabe umleitetredirect_stdout
incontextlib
quelle
Hier ist eine Variation der Antwort von Yuda Prawira :
flush()
und alle Dateiattributestderr
auch.
quelle
Basierend auf dieser Antwort: https://stackoverflow.com/a/5916874/1060344 , habe ich auf andere Weise herausgefunden, welche ich in einem meiner Projekte verwende. Für alles, was Sie ersetzen
sys.stderr
odersys.stdout
durch was Sie ersetzen , müssen Sie sicherstellen, dass der Ersatz mit derfile
Schnittstelle übereinstimmt , insbesondere wenn Sie dies tun, da stderr / stdout in einer anderen Bibliothek verwendet wird, die nicht unter Ihrer Kontrolle steht. Diese Bibliothek verwendet möglicherweise andere Methoden für Dateiobjekte.Schauen Sie sich diesen Weg an, auf dem ich immer noch alles stderr / stdout (oder eine beliebige Datei) ausführen lasse und die Nachricht mithilfe der Python-Protokollierungsfunktion an eine Protokolldatei sende (aber Sie können damit wirklich alles tun):
quelle
Sie benötigen einen Terminal-Multiplexer wie tmux oder GNU screen
Ich bin überrascht, dass ein kleiner Kommentar von Ryan Amos zu der ursprünglichen Frage die einzige Erwähnung einer Lösung ist, die allen anderen angebotenen weit vorzuziehen ist, egal wie klug die Python-Tricks sind und wie viele positive Stimmen sie erhalten haben. Neben Ryans Kommentar ist tmux eine gute Alternative zum GNU-Bildschirm.
Das Prinzip ist jedoch dasselbe: Wenn Sie jemals einen Terminaljob laufen lassen möchten, während Sie sich abmelden, gehen Sie ins Café, um ein Sandwich zu essen, gehen Sie ins Badezimmer, gehen Sie nach Hause (usw.) und verbinden Sie sich später wieder mit Ihrem Terminal-Sitzung von überall oder von jedem Computer aus, als wären Sie nie weg gewesen, Terminal-Multiplexer sind die Antwort. Stellen Sie sich diese als VNC oder Remotedesktop für Terminalsitzungen vor. Alles andere ist eine Problemumgehung. Als Bonus haben Sie nicht die letzten 18 Stunden der Verarbeitung verloren, wenn der Chef und / oder Partner hereinkommt und Sie versehentlich Ihr Terminalfenster anstelle Ihres Browserfensters mit seinem zwielichtigen Inhalt bei gedrückter Strg-Taste drücken !
quelle
Programme, die in anderen Sprachen (z. B. C) geschrieben wurden, müssen spezielle Magie (Double-Forking genannt) ausführen, um sich ausdrücklich vom Terminal zu lösen (und Zombie-Prozesse zu verhindern). Daher denke ich, dass die beste Lösung darin besteht, sie zu emulieren.
Ein Plus bei der erneuten Ausführung Ihres Programms ist, dass Sie Umleitungen in der Befehlszeile auswählen können, z
/usr/bin/python mycoolscript.py 2>&1 1>/dev/null
Weitere Informationen finden Sie in diesem Beitrag: Was ist der Grund für die Ausführung einer Doppelgabelung beim Erstellen eines Dämons?
quelle
systemd
,upstart
) oder ein anderes Dienstprogramm (daemon(1)
), um die Gabelungsplatte zu handhaben.