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

当前回答

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

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来操作路径和文件名。

其他回答

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

# 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

这是我用来打印到文件/日志的另一种方法…修改内置的打印函数,使其记录到具有当前时间戳的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

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

你的bug在别的地方:

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

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

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来操作路径和文件名。

如果重定向stdout对您的问题有效,Gringo Suave的回答很好地演示了如何进行重定向。

为了让它更简单,我使用上下文管理器创建了一个版本,使用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)

当模块以您不喜欢的方式使用它时,它对于选择性地重定向打印非常有用。唯一的缺点(在许多情况下这是解决问题的关键)是,如果想要多个具有不同stdout值的线程,它就不起作用,但这需要一种更好、更通用的方法:间接模块访问。你可以在这个问题的其他答案中看到它的实现。