问题:如何使用python将“打印”输出重定向到文件?
我想使用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)
I want to redirect the print to a .txt file using python. I have a ‘for’ loop, which will ‘print’ the output for each of my .bam file while I want to redirect ALL these output to one file. So I tried to put
f = open('output.txt','w'); sys.stdout = f
at the beginning of my script. However I get nothing in the .txt file.
My script is:
#!/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!'
........print....
........print....
So what’s the problem? Any other way besides this sys.stdout?
I need my result look like:
Filename: ERR001268.bam
Readlines finished!
Mean: 233
SD: 10
Interval is: (213, 252)
=================
How time flies. Almost 10 years ago I started to work on bioinformatics without any coding skills. Then I gave up my PhD and previous career and becomes a pure software engineer.
回答 0
最明显的方法是打印到文件对象:
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来操作路径和文件名。
The most obvious way to do this would be to print to a file object:
with open('out.txt', 'w') as f:
print >> f, 'Filename:', filename # Python 2.x
print('Filename:', filename, file=f) # Python 3.x
However, redirecting stdout also works for me. It is probably fine for a one-off script such as this:
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()
Redirecting externally from the shell itself is another good option:
./script.py > out.txt
Other questions:
What is the first filename in your script? I don’t see it initialized.
My first guess is that glob doesn’t find any bamfiles, and therefore the for loop doesn’t run. Check that the folder exists, and print out bamfiles in your script.
Also, use os.path.join and os.path.basename to manipulate paths and filenames.
回答 1
您可以通过>>
操作员重定向打印。
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')))
You can redirect print with the >>
operator.
f = open(filename,'w')
print >>f, 'whatever' # Python 2.x
print('whatever', file=f) # Python 3.x
In most cases, you’re better off just writing to the file normally.
f.write('whatever')
or, if you have several items you want to write with spaces between, like print
:
f.write(' '.join(('whatever', str(var2), 'etc')))
回答 2
Python 2或Python 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)
Python 2 or Python 3 API reference:
print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)
The file argument must be an object with a write(string)
method; if it is not present or None
, sys.stdout
will be used. Since printed arguments are converted to text strings, print()
cannot be used with binary mode file objects. For these, use file.write(...)
instead.
Since file object normally contains write()
method, all you need to do is to pass a file object into its argument.
Write/Overwrite to File
with open('file.txt', 'w') as f:
print('hello world', file=f)
Write/Append to File
with open('file.txt', 'a') as f:
print('hello world', file=f)
回答 3
这完美地工作:
import sys
sys.stdout=open("test.txt","w")
print ("hello")
sys.stdout.close()
现在,您好将被写入test.txt文件。确保关闭stdout
了close
该文件中,没有它的内容不会被保存
This works perfectly:
import sys
sys.stdout=open("test.txt","w")
print ("hello")
sys.stdout.close()
Now the hello will be written to the test.txt file. Make sure to close the stdout
with a close
, without it the content will not be save in the file
回答 4
不要print
使用logging
您可以更改sys.stdout
为指向文件,但这是处理此问题的笨拙且不灵活的方法。代替使用print
,使用logging
模块。
使用logging
,您可以像打印一样进行打印stdout
,也可以将输出写入文件。你甚至可以使用不同的消息级别(critical
,error
,warning
,info
,debug
),例如,只打印重大问题到控制台,但仍记录次要代码行动的文件。
一个简单的例子
导入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
功能的更多信息,请阅读logging
Python文档中的优秀教程。
Don’t use print
, use logging
You can change sys.stdout
to point to a file, but this is a pretty clunky and inflexible way to handle this problem. Instead of using print
, use the logging
module.
With logging
, you can print just like you would to stdout
, or you can also write the output to a file. You can even use the different message levels (critical
, error
, warning
, info
, debug
) to, for example, only print major issues to the console, but still log minor code actions to a file.
A simple example
Import logging
, get the logger
, and set the processing level:
import logging
logger = logging.getLogger()
logger.setLevel(logging.DEBUG) # process everything, even if everything isn't printed
If you want to print to stdout:
ch = logging.StreamHandler()
ch.setLevel(logging.INFO) # or any other level
logger.addHandler(ch)
If you want to also write to a file (if you only want to write to a file skip the last section):
fh = logging.FileHandler('myLog.log')
fh.setLevel(logging.DEBUG) # or any level you want
logger.addHandler(fh)
Then, wherever you would use print
use one of the logger
methods:
# 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')
To learn more about using more advanced logging
features, read the excellent logging
tutorial in the Python docs.
回答 5
最简单的解决方案不是通过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
(使脚本对所有用户可执行)
The easiest solution isn’t through python; its through the shell. From the first line of your file (#!/usr/bin/python
) I’m guessing you’re on a UNIX system. Just use print
statements like you normally would, and don’t open the file at all in your script. When you go to run the file, instead of
./script.py
to run the file, use
./script.py > <filename>
where you replace <filename>
with the name of the file you want the output to go in to. The >
token tells (most) shells to set stdout to the file described by the following token.
One important thing that needs to be mentioned here is that “script.py” needs to be made executable for ./script.py
to run.
So before running ./script.py
,execute this command
chmod a+x script.py
(make the script executable for all users)
回答 6
如果您使用的是Linux,建议您使用该tee
命令。实现是这样的:
python python_file.py | tee any_file_name.txt
如果您不想更改代码中的任何内容,我认为这可能是最好的解决方案。您也可以实现logger,但是需要对代码进行一些更改。
If you are using Linux I suggest you to use the tee
command. The implementation goes like this:
python python_file.py | tee any_file_name.txt
If you don’t want to change anything in the code, I think this might be the best possible solution. You can also implement logger but you need do some changes in the code.
回答 7
您可能不喜欢此答案,但我认为这是正确的答案。除非绝对必要,否则不要更改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()
You may not like this answer, but I think it’s the RIGHT one. Don’t change your stdout destination unless it’s absolutely necessary (maybe you’re using a library that only outputs to stdout??? clearly not the case here).
I think as a good habit you should prepare your data ahead of time as a string, then open your file and write the whole thing at once. This is because input/output operations are the longer you have a file handle open, the more likely an error is to occur with this file (file lock error, i/o error, etc). Just doing it all in one operation leaves no question for when it might have gone wrong.
Here’s an example:
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')
And then when you’re all done collecting your “data lines” one line per list item, you can join them with some '\n'
characters to make the whole thing outputtable; maybe even wrap your output statement in a with
block, for additional safety (will automatically close your output handle even if something goes wrong):
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!!!"
However if you have lots of data to write, you could write it one piece at a time. I don’t think it’s relevant to your application but here’s the 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()
回答 8
如果重定向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
,但这需要一种更好,更通用的方法:间接模块访问。您可以在此问题的其他答案中看到其实现。
If redirecting stdout
works for your problem, Gringo Suave’s answer is a good demonstration for how to do it.
To make it even easier, I made a version utilizing contextmanagers for a succinct generalized calling syntax using the with
statement:
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
To use it, you just do the following (derived from Suave’s example):
with open('out.txt', 'w') as outfile:
with redirected_stdout(outfile):
for i in range(2):
print('i =', i)
It’s useful for selectively redirecting print
when a module uses it in a way you don’t like. The only disadvantage (and this is the dealbreaker for many situations) is that it doesn’t work if one wants multiple threads with different values of stdout
, but that requires a better, more generalized method: indirect module access. You can see implementations of that in other answers to this question.
回答 9
更改sys.stdout的值不会更改所有要打印的调用的目的地。如果您使用其他方式更改打印目的地,则将获得相同的结果。
您的错误在其他地方:
- 可能是您针对问题删除的代码中(打开呼叫的文件名来自哪里?)
- 也可能是您不等待数据被刷新:如果在终端上打印,则每隔一行后都会刷新数据,但是如果您打印到文件,则仅在标准输出缓冲区已满(4096字节)时才刷新数据在大多数系统上)。
Changing the value of sys.stdout does change the destination of all calls to print. If you use an alternative way to change the destination of print, you will get the same result.
Your bug is somewhere else:
- it could be in the code you removed for your question (where does filename come from for the call to open?)
- it could also be that you are not waiting for data to be flushed: if you print on a terminal, data is flushed after every new line, but if you print to a file, it’s only flushed when the stdout buffer is full (4096 bytes on most systems).
回答 10
扩展循环打印功能的东西
x = 0
while x <=5:
x = x + 1
with open('outputEis.txt', 'a') as f:
print(x, file=f)
f.close()
Something to extend print function for loops
x = 0
while x <=5:
x = x + 1
with open('outputEis.txt', 'a') as f:
print(x, file=f)
f.close()