如何使用python将“打印”输出重定向到文件?


181

我想使用python将打印重定向到.txt文件。我有一个“ for”循环,当我要将所有这些输出重定向到一个文件时,它将“打印”每个.bam文件的输出。所以我试着把

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

在我的脚本的开头。但是,.txt文件中什么也没有。我的脚本是:

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

所以有什么问题?除了此sys.stdout以外,还有其他方法吗?

我需要我的结果看起来像:

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

7
为什么不使用f.write(data)
伊兰·齐默曼

是的,但是每个bam文件都有几个数据(平均值,SD,间隔...),如何将这些数据一一放入?
2011年

f.write(line)-在末尾插入换行符。
伊兰·齐默曼

8
@Eran Zimmerman:f.write(line)不向数据添加换行符。
hughdbrown 2011年

你说得对,我的坏。f.write(line+'\n')但是,总能总是..
伊兰·齐默尔曼

Answers:


270

最明显的方法是打印到文件对象:

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

但是,重定向标准输出对我也有效。像这样的一次性脚本可能很好:

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

从外壳本身进行外部重定向是另一个不错的选择:

./script.py > out.txt

其他问题:

脚本中的第一个文件名是什么?我看不到它已初始化。

我的第一个猜测是glob找不到任何bamfile,因此for循环不会运行。检查文件夹是否存在,并在脚本中打印出bamfiles。

另外,使用os.path.join和os.path.basename来操作路径和文件名。


您的代码的第8行使用名为filename的变量,但尚未创建。在循环的后面,您将再次使用它,但是不相关。
Gringo Suave

2
如果不需要的话,更改sys.stdout的错误做法。
机器渴望

3
@my我不认为这样的简单脚本不好。
Gringo Suave

4
+1哈哈,好吧,我可以投票赞成,因为如果您绝对必须以错误的方式进行操作,那么这是正确的方法。但是我仍然说您应该使用常规文件输出来进行操作。
渴望

1
如何在控制台上重定向和打印输出?似乎在重定向stdrr时无法显示Python中的“ print()”吗?
exteral

70

您可以通过>>操作员重定向打印。

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

在大多数情况下,最好只是正常地写入文件。

f.write('whatever')

或者,如果您要编写多个项目,并且之间要留有空格,例如print

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

2
如果有很多输出语句,这些语句会很快变旧。海报的原始想法是有效的;脚本还有其他问题。
Gringo Suave

1
Poster的原始想法绝对无效。这里没有理由重定向stdout,因为他已经将数据放入变量中。
机器渴望

我认为他的意思是“技术上有效”,因为实际上您可以重定向sys.stdout,而不是一个好主意。
2011年

35

Python 2Python 3 API参考:

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

文件参数必须是与对象write(string)方法; 如果不存在或Nonesys.stdout将使用。由于打印的参数将转换为文本字符串,print()因此不能与二进制模式文件对象一起使用。对于这些,请file.write(...)改用。

由于文件对象通常包含write()方法,因此您要做的就是将文件对象传递到其参数中。

写入/覆盖文件

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

写入/附加到文件

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

2
我只是感到困惑,为什么那些早期答案中的一些sys.stdout
Yeo 2016年

35

这完美地工作:

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

现在,您好将被写入test.txt文件。确保关闭stdoutclose该文件中,没有它的内容不会被保存


3
但是即使执行sys.stdout.close(),如果您在python shell中键入任何内容,也会显示ValueError: I/O operation on closed file. imgur.com/a/xby9P错误。解决此问题的最佳方法是遵循@Gringo Suave发布的内容
Mourya

24

不要print使用logging

您可以更改sys.stdout为指向文件,但这是处理此问题的笨拙且不灵活的方法。代替使用print,使用logging模块。

使用logging,您可以像打印一样进行打印stdout,也可以将输出写入文件。你甚至可以使用不同的消息级别(criticalerrorwarninginfodebug),例如,只打印重大问题到控制台,但仍记录次要代码行动的文件。

一个简单的例子

导入logging,获取logger并设置处理级别:

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

如果要打印到标准输出:

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

如果还要写入文件(如果只想写入文件,请跳过最后一部分):

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

然后,无论在何处使用,都可以使用print以下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')

要了解有关使用更多高级logging功能的更多信息,请阅读loggingPython文档中的优秀教程


嗨,我想使用此日志记录将控制台数据写入日志文件,时间与获取数据的时间相同。但是我无法正确理解日志记录功能或库。您能帮我吗
哈里斯

@haris通读Python文档的日志教程,并查看有关Stack Overflow的其他问题的示例(有很多)。如果仍然无法正常工作,请提出一个新问题。
jpyams

12

最简单的解决方案不是通过python;它通过外壳。从文件的第一行(#!/usr/bin/python),我猜您是在UNIX系统上。只需像平常一样使用print语句即可,而根本不会在脚本中打开文件。当您运行文件时,而不是

./script.py

运行文件,使用

./script.py > <filename>

<filename>用您要输出进入的文件名替换。该>令牌告诉(大多数)shell将stdout设置为以下令牌描述的文件。

这里需要提及的一件事是,必须使“ script.py”成为可执行文件./script.py才能运行。

因此,在运行之前./script.py,请执行以下命令

chmod a+x script.py (使脚本对所有用户可执行)


3
./script.py> <文件名> 2>&1您还需要捕获stderr。2>&1会这样做
rtaft

1
@rtaft为什么?这个问题特别想将输出print传递到文件中。期望stdout(堆栈跟踪等)仍然可以打印到终端是合理的。
亚伦·迪富

他说这没用,我的也没用。后来我发现我正在使用的这个应用程序已配置为将所有内容定向到stderr ... idk为什么。
rtaft

5

如果您使用的是Linux,建议您使用该tee命令。实现是这样的:

python python_file.py | tee any_file_name.txt

如果您不想更改代码中的任何内容,我认为这可能是最好的解决方案。您也可以实现logger,但是需要对代码进行一些更改。


1
大; 正在寻找它
Vicrobot '19

4

您可能不喜欢此答案,但我认为这是正确的答案。除非绝对必要,否则不要更改stdout的目的地(也许您使用的是仅向stdout输出的库?在此情况下显然不是这种情况)。

我认为,作为一种好习惯,您应该以字符串的形式提前准备数据,然后打开文件并立即编写整个文件。这是因为输入/输出操作打开文件句柄的时间越长,此文件发生错误的可能性就越大(文件锁定错误,I / O错误等)。只需在一个操作中完成所有操作,就可以毫无疑问地确定何时出错。

这是一个例子:

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

然后,当您完成每个列表项的一行数据行的收集时,您可以将它们与某些'\n'字符连接起来,以使整个内容可输出;也许甚至将输出语句包装在一个with块中,以提高安全性(即使出现问题,也会自动关闭输出句柄):

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

但是,如果您要写入大量数据,则可以一次写入一份。我认为这与您的应用程序无关,但是这是替代方法:

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

1
具有磁盘缓存的原始性能应该是可以接受的。但是,这种解决方案的缺点是,如果有很多输出,则会增加内存需求。尽管这里不必担心,但是最好尽可能避免这种情况。同样的想法如使用x范围(PY3范围),而不是范围等
外国佬倜傥

@Gringo:他没有指定此要求。我很少将足够的数据写到与之相关的文件中。这与xrange不同,因为xrange不处理文件I / O。磁盘缓存可能会有所帮助,但是对于大量代码而言,保持文件句柄打开仍然不是一个好习惯。
机器渴望

1
您的评论自相矛盾。坦白地说,这两种方法的性能方面都与非大量数据无关。xrange肯定是相似的,它一次只能工作一次,而不是一次在内存中工作。也许生成器与列表是一个更好的例子。
Gringo Suave

@Gringo:我看不到我的评论如何与自己矛盾。也许性能方面无关紧要,长时间打开文件句柄总是会增加出错的风险。在程序中,文件I / O总是比在您自己的程序中执行某件事本质上更具风险,因为这意味着您必须通过OS伸出手并弄乱文件锁。打开文件的时间越短越好,这仅仅是因为您不从代码中控制文件系统。xrange是不同的,因为它与文件I / O无关,仅供参考,我也很少使用xrange。欢呼声
机器渴望

2
@Gringo:感谢您的批评,并进行了热烈的辩论。尽管我们在某些方面不同意,但我仍然尊重您的观点,因为很明显您有充分的理由采取立场。感谢您合理地结束并祝您度过一个愉快的夜晚。:P
渴望的机器,

2

如果重定向stdout可以解决您的问题,则Gringo Suave的答案很好地说明了如何实现。

为了使操作更简单,我使用以下语句制作了一个版本,该版本利用contextmanagers进行了简洁的通用调用语法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

要使用它,只需执行以下操作(源自Suave的示例):

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

print当模块以您不喜欢的方式使用它时,对选择性重定向很有用。唯一的缺点(这在很多情况下都是大问题),如果想要多个具有不同值的线程是行不通的stdout,但这需要一种更好,更通用的方法:间接模块访问。您可以在此问题的其他答案中看到其实现。


0

更改sys.stdout的值不会更改所有要打印的调用的目的地。如果您使用其他方式更改打印目的地,则将获得相同的结果。

您的错误在其他地方:

  • 可能是您针对问题删除的代码中(打开呼叫的文件名来自哪里?)
  • 也可能是您不等待数据被刷新:如果在终端上打印,则每隔一行后都会刷新数据,但是如果您打印到文件,则仅在标准输出缓冲区已满(4096字节)时才刷新数据在大多数系统上)。

-1

扩展循环打印功能的东西

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

使用时无需使用while且无需关闭文件with
DanielStracaboško17年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.