簡體   English   中英

如何將“打印”output 重定向到文件?

[英]How to redirect 'print' output to a file?

我想使用 Python 將打印重定向到 a.txt 文件。我有一個for循環,它將為每個 my.bam 文件print output,而我想將所有output 重定向到一個文件。 所以我試着把:

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

但是,重定向標准輸出也適用於我。 對於像這樣的一次性腳本來說可能沒問題:

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

從外殼本身外部重定向是另一種選擇,通常更可取:

./script.py > out.txt

其他問題:

腳本中的第一個文件名是什么? 我沒有看到它初始化。

我的第一個猜測是 glob 找不到任何 bamfiles,因此 for 循環不會運行。 檢查文件夾是否存在,並在腳本中打印出 bamfiles。

此外,使用os.path.join 和 os.path.basename來操作路徑和文件名。

您可以使用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')))

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)

這完美地工作:

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

現在 hello 將寫入 test.txt 文件。 確保關閉的stdout與一個close的文件,沒有它的內容不會被保存

不要使用print ,使用logging

您可以將sys.stdout更改為指向一個文件,但這是處理此問題的一種非常笨拙且不靈活的方法。 不要使用print ,而是使用logging模塊。

使用logging ,您可以像打印stdout一樣打印,也可以將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教程

最簡單的解決方案不是通過 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 (使腳本對所有用戶都可執行)

如果您使用的是 Linux,我建議您使用tee命令。 實現是這樣的:

python python_file.py | tee any_file_name.txt

如果您不想更改代碼中的任何內容,我認為這可能是最好的解決方案。 您也可以實現記錄器,但您需要對代碼進行一些更改。

您可能不喜歡這個答案,但我認為這是正確的答案。 除非絕對必要,否則不要更改您的標准輸出目的地(也許您使用的庫只輸出到標准輸出???在這里顯然不是這種情況)。

我認為作為一個好習慣,您應該提前將數據准備為字符串,然后打開文件並立即寫入整個內容。 這是因為輸入/輸出操作打開文件句柄的時間越長,該文件發生錯誤的可能性就越大(文件鎖定錯誤、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()

如果重定向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)

當模塊以您不喜歡的方式使用它時,它對於選擇性地重定向print很有用。 唯一的缺點(這是許多情況下的交易破壞者)是,如果想要多個線程具有不同的stdout值,它就不起作用,但這需要一種更好、更通用的方法:間接模塊訪問。 你可以在這個問題的其他答案中看到它的實現。

我過去使用 output 一些字典的東西如下:

# 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

在 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 ,則other_fnprint通常不受影響。 如果我們想影響所有打印調用,我們可以使用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))

我可以使用以下方法破解它。 它將使用此打印功能而不是內置打印功能並將內容保存到文件中。

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

這是我用於打印到文件\/日志的另一種方法...修改內置打印功能,使其使用當前時間戳記錄到臨時目錄中的文件,並打印到標准輸出。 在腳本中這樣做的唯一真正好處是不必去修改現有的打印語句。

print('test')

更改 sys.stdout 的值確實會更改所有打印調用的目標。 如果您使用替代方法更改打印目的地,您將獲得相同的結果。

您的錯誤在其他地方:

  • 它可能在你為你的問題刪除的代碼中(文件名從哪里來調用打開?)
  • 也可能是您沒有等待刷新數據:如果您在終端上打印,則在每個新行之后刷新數據,但如果您打印到文件,則僅在 stdout 緩沖區已滿(4096 字節)時才刷新數據在大多數系統上)。

為循環擴展打印功能的東西

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM