306 votes

Comment rediriger la sortie 'print' vers un fichier ?

Je veux rediriger l'impression vers un fichier .txt en utilisant Python. J'ai un for boucle, qui print la sortie pour chacun de mes fichiers .bam alors que je veux rediriger todo dans un seul fichier. J'ai donc essayé de mettre :

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

au début de mon script. Cependant, je n'obtiens rien dans le fichier .txt. Mon script est :

#!/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!'

Alors quel est le problème ? Un autre moyen que celui-ci sys.stdout ?

Je veux que mon résultat ressemble à :

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

15voto

Aaron Dufour Points 6912

La solution la plus simple ne passe pas par Python, mais par le shell. A partir de la première ligne de votre fichier ( #!/usr/bin/python ) Je suppose que vous êtes sur un système UNIX. Utilisez simplement print comme vous le feriez normalement, et n'ouvrez pas du tout le fichier dans votre script. Lorsque vous allez exécuter le fichier, au lieu de

./script.py

pour exécuter le fichier, utilisez

./script.py > <filename>

où vous remplacez <filename> avec le nom du fichier dans lequel vous voulez que la sortie soit placée. Le site > indique à (la plupart) des shells de définir stdout sur le fichier décrit par le jeton suivant.

Une chose importante qui doit être mentionnée ici est que "script.py" doit être rendu exécutable pour les personnes suivantes ./script.py pour courir.

Donc avant de courir ./script.py ,exécutez cette commande

chmod a+x script.py (rendre le script exécutable pour tous les utilisateurs)

13voto

yunus Points 165

Si vous utilisez Linux, je vous suggère d'utiliser la commande tee commande. L'implémentation se déroule comme suit :

python python_file.py | tee any_file_name.txt

Si vous ne voulez rien changer dans le code, je pense que c'est la meilleure solution possible. Vous pouvez aussi implémenter le logger mais vous devez faire quelques changements dans le code.

4voto

machine yearning Points 2535

Vous n'aimerez peut-être pas cette réponse, mais je pense que c'est la BONNE. Ne changez pas la destination de votre stdout sauf si c'est absolument nécessaire (peut-être utilisez-vous une bibliothèque qui ne sort que sur stdout ??? clairement pas le cas ici).

Je pense qu'une bonne habitude à prendre est de préparer vos données à l'avance sous forme de chaîne, puis d'ouvrir votre fichier et d'écrire le tout en une seule fois. C'est parce que les opérations d'entrée/sortie sont que plus longtemps vous avez un handle de fichier ouvert, plus il est probable qu'une erreur se produise avec ce fichier (erreur de verrouillage de fichier, erreur d'entrée/sortie, etc). Le fait de tout faire en une seule opération ne laisse aucun doute sur le moment où une erreur a pu se produire.

Voici un exemple :

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

Puis, lorsque vous avez terminé de collecter vos "lignes de données", une ligne par élément de liste, vous pouvez les joindre à l'aide de la fonction '\n' pour rendre l'ensemble de l'opération accessible en sortie ; peut-être même que vous pouvez envelopper votre déclaration de sortie dans un fichier with pour plus de sécurité (il fermera automatiquement votre handle de sortie même si quelque chose ne va pas) :

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

Cependant si vous avez beaucoup de données à écrire, vous pourrait l'écrire un morceau à la fois. Je ne pense pas que ce soit pertinent pour votre demande mais voici l'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()

4voto

Graham Points 1080

Si vous redirigez stdout fonctionne pour votre problème, La réponse de Gringo Suave est une bonne démonstration de la manière de procéder.

Pour le faire encore plus facile j'ai fait une version utilisant gestionnaires de contexte pour une syntaxe d'appel généralisée et succincte utilisant l'option with déclaration :

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

Pour l'utiliser, il suffit de procéder comme suit (d'après l'exemple de Suave) :

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

C'est utile pour rediriger de manière sélective print lorsqu'un module l'utilise d'une manière qui ne vous plaît pas. Le seul inconvénient (et c'est le cas dans de nombreuses situations) est que cela ne fonctionne pas si l'on veut plusieurs threads avec des valeurs différentes de stdout mais cela nécessite une meilleure méthode, plus généralisée : l'accès indirect aux modules. Vous pouvez voir des implémentations de cette méthode dans d'autres réponses à cette question.

3voto

TCB919 Points 67

Voici une autre méthode que j'ai utilisée pour imprimer dans un fichier/journal... Modifiez la fonction print intégrée pour qu'elle enregistre dans un fichier du répertoire temp avec l'horodatage actuel, ainsi que l'impression vers stdout. Le seul avantage réel de faire cela dans un script est de ne pas avoir à aller modifier les instructions print existantes.

print('test')

test

Copie de la fonction d'impression originale vers une nouvelle variable

og_print = print
og_print('test2')

test2

Écraser la fonction d'impression existante

def print(*msg):
    '''print and log!'''
    # import datetime for timestamps
    import datetime as dt
    # convert input arguments to strings for concatenation
    message = []
    for m in msg:
        message.append(str(m))
    message = ' '.join(message)
    # append to the log file
    with open('/tmp/test.log','a') as log:
        log.write(f'{dt.datetime.now()} | {message}\n')
    # print the message using the copy of the original print function to stdout
    og_print(message)

print('test3')

test3

afficher le fichier

cat /tmp/test.log

2022-01-25 10:19:11.045062 | test3

supprimer le fichier

rm /tmp/test.log

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X