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)
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.
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')))
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
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)
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.
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 .
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()
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.
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:
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.