如何重定向'打印'输出到文件?

我想使用Python将打印重定向到一个.txt文件。我有一个for循环,它将print每个.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/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!'

那么问题是什么呢?除了sys.stdout还有其他方法吗?

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

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

你可以用file参数重定向打印(在Python 2中有>>操作符代替)。

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

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

f.write('whatever')

或者,如果你有几个项目,你想写中间有空格,比如print:

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

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

with open('out.txt', 'w') as f:
print('Filename:', filename, file=f)  # Python 3.x
print >> f, 'Filename:', filename     # Python 2.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()

自Python 3.4以来,有一个简单的上下文管理器可用来执行在标准库中:

from contextlib import redirect_stdout


with open('out.txt', 'w') as f:
with redirect_stdout(f):
print('data')

从shell本身向外部重定向是另一种选择,通常是更可取的:

./script.py > out.txt

其他问题:

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

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

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

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

你的bug在别的地方:

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

你可能不喜欢这个答案,但我认为这是正确的答案。除非绝对必要,否则不要改变你的标准输出目标(也许你使用的库只输出到标准输出??这里显然不是这样)。

我认为作为一个好习惯,你应该提前准备好你的数据作为一个字符串,然后打开你的文件,并立即写入整个东西。这是因为输入/输出操作打开文件句柄的时间越长,该文件发生错误的可能性就越大(文件锁定错误、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()

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

./script.py

要运行该文件,请使用

./script.py > <filename>

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

这里需要提到的一件重要的事情是,“script.py”必须是可执行的,以便./script.py能够运行。

所以在运行./script.py之前,执行这个命令

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

这是完美的:

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

现在hello将被写入test.txt文件。确保用close关闭stdout,没有它,内容将不会保存在文件中

Python 2Python 3 API引用:

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

文件参数必须是具有write(string)方法的对象;如果它不存在或None,将使用sys.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)

它扩展了循环的打印函数

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

不要使用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特性的信息,请阅读优秀的Python文档中的logging教程

如果你正在使用Linux,我建议你使用tee命令。实现如下所示:

python python_file.py | tee any_file_name.txt

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

如果重定向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时,它对于选择性地重定向print非常有用。唯一的缺点(这在许多情况下是交易的破坏因素)是,如果一个人想要多个具有不同stdout值的线程,它就不起作用,但这需要一个更好、更通用的方法:间接模块访问。你可以在这个问题的其他答案中看到它的实现。

在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打印只切换输出,因为打印在全局作用域中被重新分配。如果在函数中赋值打印,则other_fn中的打印通常不受影响。如果想影响所有打印调用,可以使用全球关键字:

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

我能够破解这个使用以下方法。它将使用这个打印函数而不是内置的打印函数,并将内容保存到一个文件中。

from __future__ import print_function
import builtins as __builtin__


log = open("log.txt", "a")


def print(*args):
newLine = ""
for item in args:
newLine = newLine + str(item) + " "
newLine = (
newLine
+ """
"""
)
log.write(newLine)
log.flush()
__builtin__.print(*args)
return

这是我用来打印到文件/日志的另一种方法…修改内置的打印函数,使其记录到具有当前时间戳的temp目录中的文件,并打印到stdout。在脚本中这样做的唯一真正好处是不需要去修改现有的打印语句。

print('test')
test

复制原始打印函数到新变量

og_print = print
og_print('test2')
test2

覆盖现有的打印功能

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

显示文件

cat /tmp/test.log
2022-01-25 10:19:11.045062 | test3

删除文件

rm /tmp/test.log

我在过去用来输出一些字典的东西如下:

# sample dictionary
the_dict = {'a': 'no', 'c': 'yes', 'b': 'try again'}


# path to output to
dict_path = "D:/path.txt"


# script to output file
with open(dict_path, "w") as f:
for idx, data in the_dict.items():
print(idx, data, file=f)

输出的文件如下所示:

a no
c yes
b try again