Hoe ‘afdruk’-uitvoer omleiden naar een bestand?

Ik wil de afdruk omleiden naar een .txt-bestand met Python. Ik heb een for-lus, die de uitvoer voor elk van mijn .bam-bestanden printterwijl ik alle-uitvoer naar één bestand wil omleiden. Dus ik probeerde te zetten:

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

aan het begin van mijn script. Ik krijg echter niets in het .txt-bestand.
Mijn script is:

#!/usr/bin/python
import os,sys
import subprocess
import glob
from os import path
f = open('output.txt','w')
sys.stdout = f
path= '/home/xxx/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!'

Dus wat is het probleem? Een andere manier dan deze sys.stdout?

Ik wil dat mijn resultaat eruitziet als:

Filename: ERR001268.bam
Readlines finished!
Mean: 233
SD: 10
Interval is: (213, 252)

Antwoord 1, autoriteit 100%

De meest voor de hand liggende manier om dit te doen is door naar een bestandsobject af te drukken:

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

Het omleiden van stdout werkt echter ook voor mij. Het is waarschijnlijk prima voor een eenmalig script zoals dit:

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()

Sinds Python 3.4 is er een eenvoudige contextmanager beschikbaar om dit te doen in de standaardbibliotheek:

from contextlib import redirect_stdout
with open('out.txt', 'w') as f:
    with redirect_stdout(f):
        print('data')

Extern omleiden vanuit de shell zelf is een andere optie, en heeft vaak de voorkeur:

./script.py > out.txt

Overige vragen:

Wat is de eerste bestandsnaam in je script? Ik zie het niet geïnitialiseerd.

Mijn eerste gok is dat glob geen bam-bestanden vindt, en daarom wordt de for-lus niet uitgevoerd. Controleer of de map bestaat en print de bambestanden in je script.

Gebruik ook os.path.join en os.path.basenameom paden en bestandsnamen te manipuleren.


Antwoord 2, autoriteit 24%

Je kunt print omleiden met het argument file(in Python 2 was er in plaats daarvan de operator >>).

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

In de meeste gevallen kunt u beter gewoon naar het bestand schrijven.

f.write('whatever')

of, als u meerdere items wilt schrijven met spaties ertussen, zoals print:

f.write(' '.join(('whatever', str(var2), 'etc')))

Antwoord 3, autoriteit 12%

Python 2of Python 3API-referentie:

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

Het argument bestandmoet een object zijn met een write(string)-methode; als het niet aanwezig is of None, sys.stdoutzal worden gebruikt. Aangezien afgedrukte argumenten worden geconverteerd naar tekenreeksen, kan print()niet worden gebruikt met bestandsobjecten in binaire modus. Gebruik hiervoor in plaats daarvan file.write(...).

Aangezien bestandsobjectnormaal gesproken write()-methode, hoeft u alleen maar een bestand objectin zijn argument.

Schrijven/overschrijven naar bestand

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

Schrijven/toevoegen aan bestand

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

Antwoord 4, autoriteit 11%

Dit werkt perfect:

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

De hallo wordt nu naar het bestand test.txt geschreven. Zorg ervoor dat u de stdoutsluit met een close, anders wordt de inhoud niet opgeslagen in het bestand


Antwoord 5, autoriteit 9%

Gebruik printniet, gebruik logging

Je kunt sys.stdoutwijzigen om naar een bestand te verwijzen, maar dit is een behoorlijk onhandige en inflexibele manier om dit probleem op te lossen. Gebruik in plaats van printde logging-module.

Met loggingkunt u afdrukken zoals u zou doen met stdout, of u kunt de uitvoer ook naar een bestand schrijven. U kunt zelfs de verschillende berichtniveaus gebruiken (critical, error, warning, info, debug) om bijvoorbeeld alleen grote problemen naar de console af te drukken, maar nog steeds kleine codeacties in een bestand te loggen.

Een eenvoudig voorbeeld

Importeer logging, haal de loggerop en stel het verwerkingsniveau in:

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

Als u naar stdout wilt afdrukken:

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

Als je ook naar een bestand wilt schrijven (als je alleen naar een bestand wilt schrijven, sla dan de laatste sectie over):

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

Vervolgens, waar je ook printzou gebruiken, gebruik een van de 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')

Voor meer informatie over het gebruik van meer geavanceerde logging-functies, lees de uitstekende loggingtutorial in de Python-documenten.


Antwoord 6, autoriteit 4%

De gemakkelijkste oplossing is niet via python; het is door de schil. Vanaf de eerste regel van je bestand (#!/usr/bin/python) vermoed ik dat je op een UNIX-systeem zit. Gebruik gewoon printinstructies zoals je normaal zou doen, en open het bestand helemaal niet in je script. Wanneer u het bestand gaat uitvoeren, in plaats van

./script.py

gebruik om het bestand uit te voeren

./script.py > <filename>

waar je <filename>vervangt door de naam van het bestand waar de uitvoer naar toe moet. Het >token vertelt (de meeste) shells om stdout in te stellen op het bestand beschreven door het volgende token.

Een belangrijk ding dat hier moet worden vermeld, is dat “script.py” uitvoerbaar moet worden gemaakt voordat ./script.pykan worden uitgevoerd.

Dus voordat u ./script.pyuitvoert, voert u deze opdracht uit

chmod a+x script.py
(maak het script uitvoerbaar voor alle gebruikers)


Antwoord 7, autoriteit 3%

Als je Linux gebruikt, raad ik je aan om het teecommando te gebruiken. De implementatie gaat als volgt:

python python_file.py | tee any_file_name.txt

Als je niets in de code wilt veranderen, denk ik dat dit de best mogelijke oplossing is. U kunt ook een logger implementeren, maar u moet enkele wijzigingen in de code aanbrengen.


Antwoord 8

Misschien vind je dit antwoord niet leuk, maar ik denk dat het het JUISTE antwoord is. Wijzig uw stdout-bestemming niet tenzij het absoluut noodzakelijk is (misschien gebruikt u een bibliotheek die alleen naar stdout voert??? hier duidelijk niet het geval).

Ik denk dat het een goede gewoonte is om je gegevens van tevoren als een string voor te bereiden, dan je bestand te openen en alles in één keer te schrijven. Dit komt omdat invoer-/uitvoerbewerkingen zijn, hoe langer u een bestandshandle open hebt, hoe groter de kans dat er een fout optreedt met dit bestand (bestandsvergrendelingsfout, i/o-fout, enz.). Gewoon alles in één handeling doen, laat er geen twijfel over bestaan wanneer het mis zou kunnen zijn gegaan.

Hier is een voorbeeld:

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')

en dan wanneer u klaar bent met het verzamelen van uw “data-lijnen” One-regel per lijstitem, kunt u zich bij hen voegen met een '\n'tekens om het hele ding uit te voeren; Misschien zelfs uw uitvoerrekening in een withblok, voor extra veiligheid (zal automatisch uw uitvoerhandgreep sluiten, zelfs als er iets misgaat):

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!!!"

Als u echter veel gegevens hebt om te schrijven, kunt u één stuk tegelijk schrijven. Ik denk niet dat het relevant is voor uw toepassing, maar hier is het alternatief:

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()

Antwoord 9

Als u stdoutWERKEN VOOR UW PROBLEEM, GRINGO SUAVER’S ANTWOORD is een goede demonstratie voor Hoe het te doen.

Om het nog eenvoudiger te maken , heb ik een versie gemaakt met behulp van ContextManagers voor een beknopte gegeneraliseerde bellende syntaxis met behulp van de withverklaring:

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

Om het te gebruiken, doe je gewoon het volgende (afgeleid van het voorbeeld van Suave):

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

Het is handig voor het selectief omleiden printWanneer een module het gebruikt op een manier die u niet leuk vindt. Het enige nadeel (en dit is de dealbreaker voor veel situaties) is dat het niet werkt als iemand meerdere draden wil met verschillende waarden van stdout, maar dat vereist een betere, meer gegeneraliseerde methode: indirecte module toegang. U kunt de implementaties van die in andere antwoorden op deze vraag zien.


Antwoord 10

Wijzig de waarde van Sys.stdout verandert de bestemming van alle oproepen om af te drukken. Als u een alternatieve manier gebruikt om de bestemming van de afdruk te wijzigen, krijgt u hetzelfde resultaat.

Uw bug is ergens anders:

  • Het kan in de code zijn die u hebt verwijderd voor uw vraag (waar komt de bestandsnaam vandaan voor de oproep om te openen?)
  • Het kan ook zijn dat u niet op de gewenste gegevens wacht: als u op een terminal afdrukt, worden gegevens na elke nieuwe regel gespoeld, maar als u naar een bestand afdrukt, wordt het alleen gespoeld wanneer de STDOUT-buffer vol is (4096 bytes op de meeste systemen).

Antwoord 11

In Python 3 kunt u opnieuw toewijzen print:

#!/usr/bin/python3
def other_fn():
    #This will use the print function that's active when the function is called
    print("Printing from function")
file_name = "test.txt"
with open(file_name, "w+") as f_out:
    py_print = print #Need to use this to restore builtin print later, and to not induce recursion
    print = lambda out_str : py_print(out_str, file=f_out)
    #If you'd like, for completeness, you can include args+kwargs
    print = lambda *args, **kwargs : py_print(*args, file=f_out, **kwargs)
    print("Writing to %s" %(file_name))
    other_fn()  #Writes to file
    #Must restore builtin print, or you'll get 'I/O operation on closed file'
    #If you attempt to print after this block
    print = py_print
print("Printing to stdout")
other_fn() #Writes to console/stdout

Merk op dat de printvan other_fnalleen uitgangen schakelt omdat printopnieuw wordt toegewezen in de globale scope. Als we printtoewijzen binnen een functie, wordt de printin other_fnnormaal gesproken niet beïnvloed. We kunnen het trefwoord algemeengebruiken als we alle print-oproepen willen beïnvloeden:

import builtins
def other_fn():
    #This will use the print function that's active when the function is called
    print("Printing from function")
def main():
    global print #Without this, other_fn will use builtins.print
    file_name = "test.txt"
    with open(file_name, "w+") as f_out:
        print = lambda *args, **kwargs : builtins.print(*args, file=f_out, **kwargs)
        print("Writing to %s" %(file_name))
        other_fn()  #Writes to file
        #Must restore builtin print, or you'll get 'I/O operation on closed file'
        #If you attempt to print after this block
        print = builtins.print
    print("Printing to stdout")
    other_fn() #Writes to console/stdout

Persoonlijk zou ik liever de vereiste om de functie printte gebruiken omzeilen door de beschrijving van het uitvoerbestand in een nieuwe functie te bakken:

file_name = "myoutput.txt"
with open(file_name, "w+") as outfile:
    fprint = lambda pstring : print(pstring, file=outfile)
    print("Writing to stdout")
    fprint("Writing to %s" % (file_name))

Antwoord 12

Iets om de printfunctie voor loops uit te breiden

x = 0
while x <=5:
    x = x + 1
    with open('outputEis.txt', 'a') as f:
        print(x, file=f)
    f.close()

Other episodes