如何使用python将'print'输出重定向到文件?

时间:2011-08-22 19:47:27

标签: python file-writing

我想使用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)

12 个答案:

答案 0 :(得分:189)

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

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

然而,重定向stdout对我也有用。这对于像这样的一次性脚本来说可能很好:

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

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

./script.py > out.txt

其他问题:

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

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

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

答案 1 :(得分:59)

您可以使用>>运算符重定向打印。

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 :(得分:27)

这非常有效:

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

现在hello将被写入test.txt文件。请务必使用stdout关闭close,如果没有,则内容不会保存在文件中

答案 3 :(得分:27)

  

Python 2Python 3 API参考:

     

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

     
    

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

  

由于file object通常包含write()方法,因此您需要做的就是将file object传递给其参数。

写入/覆盖文件

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

写入/附加到文件

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

答案 4 :(得分:14)

请勿使用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

如果要打印到stdout:

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功能,请阅读优秀的logging tutorial in the Python docs

答案 5 :(得分:13)

最简单的解决方案不是通过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 (使脚本可执行所有用户)

答案 6 :(得分: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()

答案 7 :(得分:2)

如果你使用linux我建议你使用tee命令,实现就像这个python python_file.py |tee any_file_name.txt 如果您不想更改代码中的任何内容,我认为这可能是最好的解决方案,您也可以实现记录器,但是您需要对代码进行一些更改。

答案 8 :(得分:0)

更改sys.stdout的值会更改所有要打印的调用的目标。如果您使用另一种方法来更改打印目的地,您将得到相同的结果。

你的错误在其他地方:

  • 它可能出在你为你的问题删除的代码中(文件名来自于打开的调用?)
  • 也可能是您没有等待刷新数据:如果您在终端上打印,则在每个新行之后刷新数据,但是如果您打印到文件,则仅在stdout缓冲区已满时刷新(大多数系统上为4096字节)。

答案 9 :(得分:0)

如果重定向stdout可以解决您的问题,那么Gringo Suave's answer就是一个很好的演示方法。

为了使其更容易使用,我使用with语句使用contextmanagers进行了简化的广义调用语法的版本:

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值的线程是行不通的,但这需要一种更好,更通用的方法:间接模块访问。您可以在此问题的其他答案中看到该实现。

答案 10 :(得分:0)

在python 3中,您可以重新分配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

请注意,other_fn中的 print 仅切换输出,因为 print 是在全局范围内重新分配的。如果我们在函数中分配 print ,则other_fn中的 print 通常不会受到影响。如果我们想影响所有 print 调用,我们可以使用 global 关键字:

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

我个人更希望通过将输出文件描述符烘焙到新函数中来回避使用print函数的要求:

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

答案 11 :(得分:-1)

扩展循环打印功能的东西

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