web-dev-qa-db-fra.com

Comment rediriger la sortie 'print' vers un fichier en utilisant python?

Je souhaite rediriger l'impression vers un fichier .txt à l'aide de python. J'ai une boucle «pour» qui «imprime» la sortie de chacun de mes fichiers .bam pendant que je veux rediriger TOUTES ces sorties vers un fichier. Alors j'ai essayé de mettre

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

au début de mon script. Cependant, je n'ai 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/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....

Donc quel est le problème? Un autre moyen en dehors de ce sys.stdout?

J'ai besoin que mon résultat ressemble à:

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

La manière la plus évidente de le faire serait d’imprimer dans un objet fichier:

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

Cependant, la redirection de stdout fonctionne également pour moi. C'est probablement bien pour un script unique comme celui-ci:

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

La redirection externe depuis le shell lui-même est une autre bonne option:

./script.py > out.txt

D'autres questions:

Quel est le premier nom de fichier dans votre script? Je ne le vois pas initialisé.

Ma première hypothèse est que glob ne trouve aucun fichier bamfile et que, par conséquent, la boucle for ne s'exécute pas. Vérifiez que le dossier existe et imprimez le fichier bamfile dans votre script.

Utilisez également os.path.join et os.path.basename pour manipuler les chemins et les noms de fichiers.

177
Gringo Suave

Vous pouvez rediriger l'impression avec l'opérateur >>.

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

Dans la plupart des cas, il vaut mieux écrire dans le fichier normalement.

f.write('whatever')

ou, si vous souhaitez écrire plusieurs éléments avec des espaces, tels que print:

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

Cela fonctionne parfaitement:

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

Maintenant, le bonjour sera écrit dans le fichier test.txt. Assurez-vous de fermer la stdout avec une close, sans cela le contenu ne sera pas sauvegardé dans le fichier

25
Pradeep Kumar

La solution la plus simple ne passe pas par python; c'est à travers la coquille. Dès la première ligne de votre fichier (#!/usr/bin/python), je suppose que vous êtes sur un système UNIX. Utilisez simplement les instructions print comme vous le feriez normalement et n'ouvrez pas le fichier du tout 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> par le nom du fichier dans lequel vous voulez que la sortie soit insérée. Le jeton > indique (à la plupart) des shells de définir stdout sur le fichier décrit par le jeton suivant.

Une chose importante à mentionner ici est que "script.py" doit être rendu exécutable pour que ./script.py puisse être exécuté.

Donc avant d'exécuter ./script.py, exécutez cette commande

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

13
Aaron Dufour

N'utilisez pas print, utilisez logging

Vous pouvez modifier sys.stdout pour qu'il pointe vers un fichier, mais il s’agit d’une manière assez maladroite et inflexible de gérer ce problème. Au lieu d'utiliser print, utilisez le module logging .

Avec logging, vous pouvez imprimer comme vous le feriez pour stdout ou vous pouvez également écrire le résultat dans un fichier. Vous pouvez même utiliser les différents niveaux de message (critical, error, warning, info, debug) pour, par exemple, uniquement imprimer les problèmes majeurs dans la console, tout en enregistrant les actions de code mineur dans un fichier.

Un exemple simple

Importez logging, obtenez la logger et définissez le niveau de traitement:

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

Si vous voulez imprimer sur stdout:

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

Si vous souhaitez également écrire dans un fichier (si vous souhaitez uniquement écrire dans un fichier, ignorez la dernière section):

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

Ensuite, partout où vous utiliseriez print, utilisez l’une des méthodes logger:

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

Pour en savoir plus sur l'utilisation de fonctionnalités logging plus avancées, lisez l'excellent tutoriel logging dans la documentation Python .

12
jpyams

Vous n'aimerez peut-être pas cette réponse, mais je pense que c'est la bonne. Ne changez pas votre destination stdout sauf en cas d'absolue nécessité (vous utilisez peut-être une bibliothèque qui n'effectue que la sortie sur stdout, ce qui n'est clairement pas le cas ici).

Je pense que, comme bonne habitude, vous devez préparer vos données à l’avance sous forme de chaîne, puis ouvrir votre fichier et écrire le tout en une fois. Cela est dû au fait que les opérations d'entrée/sortie durent longtemps et que plus le nom de fichier est ouvert, plus le risque d'erreur avec ce fichier est probable (erreur de verrouillage de fichier, erreur d'E/S, etc.). Tout faire en une seule opération ne laisse aucune question sur le moment où cela aurait mal tourné.

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

Et ensuite, lorsque vous avez terminé de collecter vos "lignes de données", une ligne par élément de liste, vous pouvez les joindre avec quelques caractères '\n' pour créer l'ensemble de la table de sortie. peut-être même envelopper votre instruction de sortie dans un bloc with, pour plus de sécurité (fermera automatiquement votre handle de sortie, même en cas de problème):

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 pourriez / les écrire un morceau à la fois. Je ne pense pas que cela soit pertinent pour votre application, 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()
4
machine yearning

si vous utilisez linux, je vous suggère d'utiliser la commande tee. L'implémentation va comme ceci: python_file.py |tee any_file_name.txt.__ python. Si vous ne voulez rien changer dans le code, je pense que cela pourrait être la meilleure solution possible, vous pouvez également mettre en œuvre un enregistreur mais vous devez faire des changements dans le code.

1
yunus

La modification de la valeur de sys.stdout modifie la destination de tous les appels à imprimer. Si vous utilisez une autre méthode pour changer la destination d'impression, vous obtiendrez le même résultat.

Votre bug est ailleurs:

  • cela pourrait être dans le code que vous avez supprimé pour votre question (d'où vient le nom de fichier pour l'appel à ouvrir?)
  • il se peut également que vous n'attendiez pas que les données soient vidées: si vous imprimez sur un terminal, les données sont vidées après chaque nouvelle ligne, mais si vous imprimez dans un fichier, elles ne le sont que lorsque le tampon stdout est plein sur la plupart des systèmes).
0
Jerome

Si la redirection stdout convient à votre problème, Réponse de Gringo Suave est une bonne démonstration de la procédure à suivre.

Pour le rendre encore plus facile, j'ai créé une version utilisant contextmanagers pour une syntaxe d'appel généralisée succincte à l'aide de l'instruction with:

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 vous suffit de procéder comme suit (dérivé de 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 sélectivement print lorsqu'un module l'utilise d'une manière que vous n'aimez pas. Le seul inconvénient (et c'est le dealbreaker dans de nombreuses situations) est que cela ne fonctionne pas si 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 au module. Vous pouvez voir des implémentations de cela dans d'autres réponses à cette question.

0
Graham