Wie leite ich die Druckausgabe mit Python in eine Datei um?

181

Ich möchte den Druck mit Python in eine TXT-Datei umleiten. Ich habe eine 'for'-Schleife, die die Ausgabe für jede meiner .bam-Dateien' druckt ', während ich ALLE diese Ausgaben in eine Datei umleiten möchte. Also habe ich versucht zu setzen

 f = open('output.txt','w'); sys.stdout = f

am Anfang meines Skripts. Allerdings bekomme ich nichts in der .txt-Datei. Mein Skript lautet:

#!/usr/bin/python

import os,sys
import subprocess
import glob
from os import path

f = open('output.txt','w')
sys.stdout = f

path= '/home/xug/nearline/bamfiles'
bamfiles = glob.glob(path + '/*.bam')

for bamfile in bamfiles:
    filename = bamfile.split('/')[-1]
    print 'Filename:', filename
    samtoolsin = subprocess.Popen(["/share/bin/samtools/samtools","view",bamfile],
                                  stdout=subprocess.PIPE,bufsize=1)
    linelist= samtoolsin.stdout.readlines()
    print 'Readlines finished!'
    ........print....
    ........print....

Also, was ist das Problem? Irgendein anderer Weg als dieses sys.stdout?

Ich muss mein Ergebnis so aussehen lassen:

Filename: ERR001268.bam
Readlines finished!
Mean: 233
SD: 10
Interval is: (213, 252)
LookIntoEast
quelle
7
Warum nicht verwenden f.write(data)?
Eran Zimmerman
Ja, aber ich habe mehrere Daten für jede BAM-Datei (Mittelwert, SD, Intervall ...). Wie kann ich diese Daten einzeln ablegen?
LookIntoEast
f.write(line)- Am Ende wird ein Zeilenumbruch eingefügt.
Eran Zimmerman
8
@Eran Zimmerman: Fügt f.write(line)den Daten keinen Zeilenumbruch hinzu.
Hughdbrown
Du hast recht, mein schlechtes. Könnte aber immer f.write(line+'\n')..
Eran Zimmerman

Antworten:

270

Der naheliegendste Weg, dies zu tun, wäre das Drucken in ein Dateiobjekt:

with open('out.txt', 'w') as f:
    print >> f, 'Filename:', filename     # Python 2.x
    print('Filename:', filename, file=f)  # Python 3.x

Das Umleiten von stdout funktioniert jedoch auch bei mir. Für ein einmaliges Skript wie dieses ist es wahrscheinlich in Ordnung:

import sys

orig_stdout = sys.stdout
f = open('out.txt', 'w')
sys.stdout = f

for i in range(2):
    print 'i = ', i

sys.stdout = orig_stdout
f.close()

Die externe Umleitung von der Shell selbst ist eine weitere gute Option:

./script.py > out.txt

Andere Fragen:

Was ist der erste Dateiname in Ihrem Skript? Ich sehe es nicht initialisiert.

Meine erste Vermutung ist, dass glob keine Bamfiles findet und daher die for-Schleife nicht ausgeführt wird. Überprüfen Sie, ob der Ordner vorhanden ist, und drucken Sie die BAM-Dateien in Ihrem Skript aus.

Verwenden Sie außerdem os.path.join und os.path.basename , um Pfade und Dateinamen zu bearbeiten .

Gringo Suave
quelle
Zeile 8 Ihres Codes verwendet eine Variable mit dem Namen Dateiname, die jedoch noch nicht erstellt wurde. Später in der Schleife verwenden Sie es erneut, aber nicht relevant.
Gringo Suave
2
Schlechte Praxis, um sys.stdout zu ändern, wenn Sie nicht müssen.
Maschinen Sehnsucht
3
@my Ich bin nicht davon überzeugt, dass es für ein einfaches Skript wie dieses schlecht ist.
Gringo Suave
4
+1 Haha, gut, du kannst mein Upvote haben, weil es der richtige Weg ist, es zu tun, wenn du es unbedingt falsch machen musst ... Aber ich sage immer noch, du solltest es mit normaler Dateiausgabe tun.
Maschinen Sehnsucht
1
Wie leite ich die Ausgabe um und drucke sie auf der Konsole aus? Scheint, dass "print ()" in Python nicht angezeigt werden kann, wenn der stdrr umgeleitet wird?
Externe
70

Sie können den Druck mit dem >>Bediener umleiten .

f = open(filename,'w')
print >>f, 'whatever'     # Python 2.x
print('whatever', file=f) # Python 3.x

In den meisten Fällen ist es besser, einfach normal in die Datei zu schreiben.

f.write('whatever')

oder, wenn Sie mehrere Elemente haben, die Sie mit Leerzeichen dazwischen schreiben möchten, wie z print.

f.write(' '.join(('whatever', str(var2), 'etc')))
agf
quelle
2
Wenn es viele Ausgabeanweisungen gibt, können diese schnell alt werden. Die ursprüngliche Idee des Plakats ist gültig; Mit dem Skript stimmt noch etwas nicht.
Gringo Suave
1
Die ursprüngliche Idee des Posters ist absolut ungültig. Es gibt hier keinen Grund, stdout umzuleiten, da er die Daten bereits in eine Variable umwandelt.
Maschinen Sehnsucht
Ich denke, er meinte "technisch gültig", indem man tatsächlich umleiten kann sys.stdout, nicht dass es eine gute Idee war.
Agf
35

Python 2- oder Python 3- API-Referenz:

print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)

Das Dateiargument muss ein Objekt mit einem sein , write(string)Verfahren; wenn es nicht vorhanden oder ist None, sys.stdoutwird verwendet. Da gedruckte Argumente in Textzeichenfolgen konvertiert werden, print()können sie nicht mit Dateiobjekten im Binärmodus verwendet werden. Verwenden Sie file.write(...)stattdessen diese.

Da das Dateiobjekt normalerweise eine write()Methode enthält , müssen Sie lediglich ein Dateiobjekt an sein Argument übergeben.

In Datei schreiben / überschreiben

with open('file.txt', 'w') as f:
    print('hello world', file=f)

Schreiben / An Datei anhängen

with open('file.txt', 'a') as f:
    print('hello world', file=f)
Yeo
quelle
2
Ich habe nur verwirrt, warum einige dieser früheren Antworten auf das globale sys.stdout
Yeo
35

Das funktioniert perfekt:

import sys
sys.stdout=open("test.txt","w")
print ("hello")
sys.stdout.close()

Jetzt wird das Hallo in die Datei test.txt geschrieben. Stellen Sie sicher, dass Sie das stdoutmit a schließen close. Ohne dieses wird der Inhalt nicht in der Datei gespeichert

Pradeep Kumar
quelle
3
Aber selbst wenn wir etwas ausführen sys.stdout.close(), wenn Sie etwas in die Python-Shell ValueError: I/O operation on closed file. eingeben, wird der Fehler als imgur.com/a/xby9P angezeigt . Der beste Weg, um damit umzugehen, ist zu folgen, was @Gringo Suave gepostet hat
Mourya
24

Nicht benutzen print, benutzenlogging

Sie können ändern sys.stdout, um auf eine Datei zu verweisen, aber dies ist eine ziemlich umständliche und unflexible Methode, um dieses Problem zu lösen. printVerwenden Sie das loggingModul anstelle von .

Mit loggingkönnen Sie wie gewohnt drucken stdoutoder die Ausgabe in eine Datei schreiben. Sie können sogar die verschiedenen Nachrichtenebene (verwenden critical, error, warning, info, debug) auf, beispielsweise nur wichtige Fragen an die Konsole drucken, aber immer noch kleinere Code Aktionen in eine Datei protokollieren.

Ein einfaches Beispiel

Importieren logging, abrufen loggerund Verarbeitungsstufe festlegen:

import logging
logger = logging.getLogger()
logger.setLevel(logging.DEBUG) # process everything, even if everything isn't printed

Wenn Sie auf stdout drucken möchten:

ch = logging.StreamHandler()
ch.setLevel(logging.INFO) # or any other level
logger.addHandler(ch)

Wenn Sie auch in eine Datei schreiben möchten (wenn Sie nur in eine Datei schreiben möchten, überspringen Sie den letzten Abschnitt):

fh = logging.FileHandler('myLog.log')
fh.setLevel(logging.DEBUG) # or any level you want
logger.addHandler(fh)

Verwenden Sie dann, wo immer Sie sie verwenden würden print, eine der folgenden loggerMethoden:

# print(foo)
logger.debug(foo)

# print('finishing processing')
logger.info('finishing processing')

# print('Something may be wrong')
logger.warning('Something may be wrong')

# print('Something is going really bad')
logger.error('Something is going really bad')

Weitere Informationen zur Verwendung erweiterter loggingFunktionen finden Sie im hervorragenden loggingTutorial in den Python-Dokumenten .

jpyams
quelle
Hallo, ich möchte diese Protokollierung zum Schreiben der Konsolendaten in die Protokolldatei mit dem Zeitpunkt verwenden, zu dem diese Daten erfasst werden. Aber ich bin nicht in der Lage, die Protokollierungsfunktion oder Bibliothek richtig zu verstehen. Können Sie mir dabei helfen
Haris
@haris Lesen Sie das Protokollierungs-Tutorial der Python-Dokumente durch und lesen Sie Beispiele in anderen Fragen zum Stapelüberlauf (es gibt viele davon). Wenn Sie es immer noch nicht zum Laufen bringen können, stellen Sie eine neue Frage.
Jpyams
12

Die einfachste Lösung ist nicht Python; Es ist durch die Schale. Aus der ersten Zeile Ihrer Datei ( #!/usr/bin/python) geht hervor, dass Sie sich auf einem UNIX-System befinden. Verwenden printSie einfach Anweisungen wie gewohnt und öffnen Sie die Datei in Ihrem Skript überhaupt nicht. Wenn Sie die Datei ausführen, anstatt

./script.py

Verwenden Sie zum Ausführen der Datei

./script.py > <filename>

wo Sie durch <filename>den Namen der Datei ersetzen, in die die Ausgabe gehen soll. Das >Token weist (die meisten) Shells an, stdout auf die durch das folgende Token beschriebene Datei zu setzen.

Eine wichtige Sache, die hier erwähnt werden muss, ist, dass "script.py" ausführbar gemacht werden muss ./script.py, damit es ausgeführt werden kann.

Führen Sie ./script.pydiesen Befehl aus, bevor Sie ihn ausführen

chmod a+x script.py (Machen Sie das Skript für alle Benutzer ausführbar.)

Aaron Dufour
quelle
3
./script.py> <Dateiname> 2> & 1 Sie müssen auch stderr erfassen. 2> & 1 werden das tun
rtaft
1
@rtaft Warum? Die Frage möchte speziell die Ausgabe von printan eine Datei weiterleiten. Es ist vernünftig zu erwarten, dass stdout (Stapelspuren und dergleichen) weiterhin auf dem Terminal gedruckt wird.
Aaron Dufour
Er sagte, es funktioniere nicht, meins auch nicht. Ich entdeckte später, dass diese App, an der ich arbeite, so konfiguriert wurde, dass sie alles an stderr ... idk weiterleitet.
Rtaft
5

Wenn Sie Linux verwenden, empfehle ich Ihnen, den teeBefehl zu verwenden. Die Implementierung sieht folgendermaßen aus:

python python_file.py | tee any_file_name.txt

Wenn Sie nichts am Code ändern möchten, ist dies möglicherweise die bestmögliche Lösung. Sie können den Logger auch implementieren, müssen jedoch einige Änderungen am Code vornehmen.

Yunus
quelle
1
großartig; suchte danach
Vicrobot
4

Diese Antwort mag Ihnen vielleicht nicht gefallen, aber ich denke, es ist die RICHTIGE. Ändern Sie Ihr stdout-Ziel nur, wenn dies unbedingt erforderlich ist (möglicherweise verwenden Sie eine Bibliothek, die nur stdout ausgibt - hier ist dies eindeutig nicht der Fall).

Ich denke, als gute Angewohnheit sollten Sie Ihre Daten im Voraus als Zeichenfolge vorbereiten, dann Ihre Datei öffnen und das Ganze auf einmal schreiben. Dies liegt daran, dass Eingabe- / Ausgabeoperationen umso wahrscheinlicher sind, je länger ein Dateihandle geöffnet ist, wenn bei dieser Datei ein Fehler auftritt (Dateisperrfehler, E / A-Fehler usw.). Nur alles in einem Arbeitsgang zu erledigen, lässt keine Frage offen, wann es schief gegangen sein könnte.

Hier ist ein Beispiel:

out_lines = []
for bamfile in bamfiles:
    filename = bamfile.split('/')[-1]
    out_lines.append('Filename: %s' % filename)
    samtoolsin = subprocess.Popen(["/share/bin/samtools/samtools","view",bamfile],
                                  stdout=subprocess.PIPE,bufsize=1)
    linelist= samtoolsin.stdout.readlines()
    print 'Readlines finished!'
    out_lines.extend(linelist)
    out_lines.append('\n')

Und wenn Sie mit dem Sammeln Ihrer " '\n'Datenzeilen " in einer Zeile pro Listenelement fertig sind, können Sie sie mit einigen Zeichen verbinden, um das Ganze als Ausgabetabelle zu erstellen. Vielleicht verpacken Sie Ihre Ausgabeanweisung withzur zusätzlichen Sicherheit sogar in einen Block (schließt automatisch Ihr Ausgabehandle, selbst wenn etwas schief geht):

out_string = '\n'.join(out_lines)
out_filename = 'myfile.txt'
with open(out_filename, 'w') as outf:
    outf.write(out_string)
print "YAY MY STDOUT IS UNTAINTED!!!"

Allerdings , wenn Sie eine Menge Daten zu schreiben haben, Sie könnten es ein Stück zu einer Zeit schreiben. Ich denke nicht, dass es für Ihre Anwendung relevant ist, aber hier ist die Alternative:

out_filename = 'myfile.txt'
outf = open(out_filename, 'w')
for bamfile in bamfiles:
    filename = bamfile.split('/')[-1]
    outf.write('Filename: %s' % filename)
    samtoolsin = subprocess.Popen(["/share/bin/samtools/samtools","view",bamfile],
                                  stdout=subprocess.PIPE,bufsize=1)
    mydata = samtoolsin.stdout.read()
    outf.write(mydata)
outf.close()
Maschinen Sehnsucht
quelle
1
Mit Disk Caching sollte die Leistung des Originals akzeptabel sein. Diese Lösung hat jedoch den Nachteil, dass der Speicherbedarf bei viel Ausgabe erhöht wird. Obwohl hier wahrscheinlich kein Grund zur Sorge besteht, ist es im Allgemeinen eine gute Idee, dies nach Möglichkeit zu vermeiden. Dieselbe Idee wie die Verwendung von xrange (py3-Bereich) anstelle von Bereich usw.
Gringo Suave
@Gringo: Er hat diese Anforderung nicht spezifiziert. Selten schreibe ich genug Daten in eine Datei, die relevant wären. Dies ist nicht die gleiche Idee wie bei xrange, da xrange sich nicht mit Datei-E / A befasst. Das Zwischenspeichern von Datenträgern kann hilfreich sein, aber es ist immer noch eine schlechte Praxis, ein Dateihandle für einen großen Codebestand offen zu halten.
Maschinen Sehnsucht
1
Ihr Kommentar widerspricht sich. Um ehrlich zu sein, ist der Leistungsaspekt beider Ansätze für nicht große Datenmengen irrelevant. xrange ist sicherlich ähnlich, es funktioniert jeweils für ein Stück anstatt für alle gleichzeitig im Speicher. Vielleicht ist ein Generator gegen eine Liste ein besseres Beispiel.
Gringo Suave
@Gringo: Ich sehe nicht, wie mein Kommentar sich selbst widerspricht. Möglicherweise ist der Leistungsaspekt nicht relevant. Wenn Sie ein Dateihandle über einen längeren Zeitraum offen halten, erhöht sich immer das Fehlerrisiko. Bei der Programmierung von Dateien ist E / A von Natur aus immer riskanter als das Ausführen von Aktionen in Ihrem eigenen Programm, da Sie sich über das Betriebssystem informieren und mit Dateisperren herumspielen müssen. Je kürzer eine Datei geöffnet ist, desto besser, einfach weil Sie das Dateisystem nicht über Ihren Code steuern. xrange ist anders, weil es nichts mit Datei-E / A zu tun hat, und zu Ihrer Information verwende ich xrange auch selten; Prost
Maschinen Sehnsucht
2
@Gringo: Ich schätze Ihre Kritik und habe die hitzige Debatte genossen. Auch wenn wir uns in einigen Punkten nicht einig waren, respektiere ich Ihre Ansichten, da klar ist, dass Sie einen guten Grund haben, Ihre Haltung einzunehmen. Vielen Dank für das vernünftige Ende und eine sehr gute Nacht. : P
Maschine Sehnsucht
2

Wenn die Umleitung stdoutfür Ihr Problem funktioniert, ist die Antwort von Gringo Suave eine gute Demonstration dafür.

Um es noch einfacher zu machen , habe ich eine Version erstellt, die Kontextmanager für eine prägnante verallgemeinerte Aufrufsyntax verwendet, indem ich die folgende withAnweisung verwende:

from contextlib import contextmanager
import sys

@contextmanager
def redirected_stdout(outstream):
    orig_stdout = sys.stdout
    try:
        sys.stdout = outstream
        yield
    finally:
        sys.stdout = orig_stdout

Um es zu verwenden, gehen Sie einfach wie folgt vor (abgeleitet von Suaves Beispiel):

with open('out.txt', 'w') as outfile:
    with redirected_stdout(outfile):
        for i in range(2):
            print('i =', i)

Dies ist nützlich, um selektiv umzuleiten, printwenn ein Modul es auf eine Weise verwendet, die Sie nicht mögen. Der einzige Nachteil (und dies ist der Dealbreaker für viele Situationen) ist, dass es nicht funktioniert, wenn mehrere Threads mit unterschiedlichen Werten von gewünscht werden stdout, dies jedoch eine bessere, allgemeinere Methode erfordert: indirekten Modulzugriff. Sie können Implementierungen davon in anderen Antworten auf diese Frage sehen.

Graham
quelle
0

Durch Ändern des Werts von sys.stdout wird das Ziel aller zu druckenden Aufrufe geändert. Wenn Sie das Druckziel auf alternative Weise ändern, erhalten Sie das gleiche Ergebnis.

Ihr Fehler ist woanders:

  • Es könnte sich um den Code handeln, den Sie für Ihre Frage entfernt haben (woher kommt der Dateiname, damit der Anruf geöffnet wird?)
  • Es kann auch sein, dass Sie nicht darauf warten, dass Daten gelöscht werden: Wenn Sie auf einem Terminal drucken, werden Daten nach jeder neuen Zeile gelöscht. Wenn Sie jedoch in eine Datei drucken, werden sie nur gelöscht, wenn der Standardpuffer voll ist (4096 Byte) auf den meisten Systemen).
Hieronymus
quelle
-1

Etwas, um die Druckfunktion für Schleifen zu erweitern

x = 0
while x <=5:
    x = x + 1
    with open('outputEis.txt', 'a') as f:
        print(x, file=f)
    f.close()
ishiry ish
quelle
Keine Notwendigkeit zu verwenden whileund keine Notwendigkeit, die Datei bei der Verwendung zu schließenwith
Daniel Stracaboško