web-dev-qa-db-ja.com

pythonを使って 'print'出力をファイルにリダイレクトするにはどうすればいいですか?

私はpythonを使って印刷を.txtファイルにリダイレクトしたいです。私はすべてのこれらの出力を1つのファイルにリダイレクトしたいのですが、私はそれぞれの.bamファイルの出力を '印刷'する 'for'ループがあります。だから私は入れてみました

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

これを行う最も明白な方法は、ファイルオブジェクトに出力することです。

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

しかし、標準出力のリダイレクトも私にとってはうまくいきます。このような1回限りのスクリプトにはおそらく大丈夫です。

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

シェル自体から外部にリダイレクトすることは、もう1つの良い選択肢です。

./script.py > out.txt

その他の質問

スクリプトの最初のファイル名は何ですか?初期化されていないようです。

私の最初の推測は、globがどんな失敗ファイルも見つけられないため、forループが実行されないことです。フォルダが存在することを確認し、スクリプト内のbamfilesを印刷します。

また、パスとファイル名を操作するには、 os.path.joinとos.path.basename を使用します。

192
Gringo Suave

>>演算子を使用して印刷をリダイレクトできます。

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

Python 2 または Python 3 APIリファレンス

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

file引数は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)
27
Yeo

これは完璧に動作します。

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

こんにちはこんにちはtest.txtファイルに書き込まれます。 stdoutは必ずcloseで閉じてください。そうしないと、内容がファイルに保存されません。

27
Pradeep Kumar

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メソッドのうちの1つを使う:

# 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チュートリアル を読んでください。

14
jpyams

最も簡単な解決策はpythonにはありません。そのシェルを通して。あなたのファイルの最初の行(#!/usr/bin/python)から、あなたはUNIXシステムにいると思います。通常どおりにprintステートメントを使用し、スクリプト内でファイルをまったく開かないでください。代わりにファイルを実行すると

./script.py

ファイルを実行するには、

./script.py > <filename>

<filename>を、出力先のファイルの名前に置き換えます。 >トークンは、(ほとんどの)シェルに、標準出力を次のトークンで示されるファイルに設定するように指示します。

ここで言及する必要がある1つの重要なことは./script.pyを実行するために "script.py"を実行可能にする必要があることです。

./script.pyを実行する前に、このコマンドを実行してください。

chmod a+x script.py(すべてのユーザーに対してスクリプトを実行可能にする)

13
Aaron Dufour

あなたはこの答えが気に入らないかもしれませんが、私はそれが正しい答えだと思います。どうしても必要な場合以外は、標準出力先を変更しないでください(おそらく標準出力にのみ出力するライブラリを使用しているのでしょうか?明らかにここでは当てはまりません)。

良い習慣として、あなたはデータを文字列として事前に準備し、それからあなたのファイルを開いて一度に全部を書くべきだと思います。これは、ファイルハンドルを開いている時間が長いほど、入出力操作によってこのファイルでエラーが発生する可能性が高いためです(ファイルロックエラー、入出力エラーなど)。 1回の操作ですべてを実行しただけで、問題が発生した可能性がある場合は問題ありません。

これが例です:

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

そして、リスト項目ごとに1行ずつ「データ行」を収集し終えたら、それらすべてを'\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!!!"

ただし、書き込むデータがたくさんある場合は、一度に1つずつ書き込むことができます。私はそれがあなたのアプリケーションに関連するとは思いませんが、これが代替案です:

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

もしあなたがlinuxを使用している場合、teeコマンドを使用することをお勧めします。コードの中で何かを変更したくない場合、実装はこのpython python_file.py |tee any_file_name.txtのようになります。コードで。

2
yunus

Sys.stdoutの値を変更すると、printへのすべての呼び出しの宛先が変わります。別の方法で印刷先を変更しても、同じ結果になります。

あなたのバグは他の場所にあります。

  • それはあなたの質問のためにあなたが削除したコードの中にあるかもしれません(filenameはどこへの呼び出しのために由来するのですか?)
  • データがフラッシュされるのを待っていない可能性もあります。端末で印刷する場合、データは新しい行ごとにフラッシュされますが、ファイルに印刷する場合は、stdoutバッファがいっぱいになったときにのみフラッシュされます(4096バイト)ほとんどのシステムで).
0
Jerome

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の値が異なる複数のスレッドが欲しい場合にはうまく行かないということです。あなたはこの質問に対する他の答えでそれの実装を見ることができます。

0
Graham