Pythonを使用して「印刷」出力をファイルにリダイレクトする方法


184

Pythonを使用して、印刷を.txtファイルにリダイレクトしたい。私は 'for'ループを持っています。これは、すべての出力を1つのファイルにリダイレクトしたい一方で、.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)

7
なぜ使用しないのf.write(data)ですか?
Eran Zimmerman Gonen

ええ、でも、BAMファイルごとにいくつかのデータ(平均、SD、間隔...)があります。これらのデータを1つずつ配置するにはどうすればよいですか?
LookIntoEast 2011

f.write(line)-最後に改行を挿入します。
Eran Zimmerman Gonen

8
@Eran Zimmerman:f.write(line)データに改行を追加しません。
hughdbrown、2011

あなたは正しい、私の悪い。いつもでしたf.write(line+'\n')、しかし...
エランジマーマンゴネン

回答:


274

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

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

ただし、stdoutのリダイレクトも機能します。次のような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()

シェル自体から外部にリダイレクトするのも良い方法です。

./script.py > out.txt

その他の質問:

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

私の最初の推測では、globはbamfileを見つけられないため、forループは実行されません。フォルダが存在することを確認し、スクリプトでbamfilesを出力します。

また、os.path.joinおよびos.path.basenameを使用して、パスとファイル名を操作します。


コードの8行目はfilenameという名前の変数を使用していますが、まだ作成されていません。ループの後半で再び使用しますが、関係ありません。
Gringo Suave 2011

2
必要がない場合にsys.stdoutを変更するのは悪い習慣です。
マシン

3
@myこのような単純なスクリプトには適していないと思います。
Gringo Suave 2011

4
+1ハハハハハハハハハハハハハハハハハハハハハハハハハハハハハハハハハハハマスマスミナナマナ!!でも、通常のファイル出力でどうぞ
憧れるマシン

1
コンソールに出力をリダイレクトして印刷する方法は?stdrrがリダイレクトされると、Pythonの「print()」を表示できないようです。
exteral

70

>>オペレーターで印刷をリダイレクトできます。

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

2
多くの出力ステートメントがある場合、これらは古くなります。ポスターのオリジナルのアイデアは有効です。スクリプトに何か問題があります。
Gringo Suave 2011

1
ポスターの元のアイデアは絶対に無効です。ここでstdoutをリダイレクトする理由はありません。なぜなら、彼はすでにデータを変数に入れているからです。
憧れる機械

彼は「技術的に有効」を意味sys.stdoutしていたと思います。実際にはリダイレクトできますが、それは良いアイデアではありませんでした。
agf 2011

35

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)

2
なぜこれらの以前の答えのいくつかがグローバルにサルのパッチを当てることであったのか混乱しましたsys.stdout:(
Yeo

35

これは完全に機能します:

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

こんにちは、test.txtファイルに書き込まれます。閉じていることを確認しstdoutclose、それなしでコンテンツがファイルに保存されません


3
しかしsys.stdout.close()、実行しても、Pythonシェルで何かを入力すると、ValueError: I/O operation on closed file. imgur.com / a / xby9Pのようなエラーが表示されます。これを処理する最良の方法は、@ Gringo Suaveが投稿した内容に従うことです
Mourya

24

使用せず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

stdoutに出力する場合:

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ドキュメントの優れたチュートリアルをご覧ください


こんにちは。このログを使用して、コンソールデータをログファイルに書き込みます。しかし、ロギング機能やライブラリを正しく理解できません。これで私を助けることができます
ハリス

@haris Pythonドキュメントのロギングチュートリアルを読み、Stack Overflowに関する他の質問の例を確認してください(たくさんあります)。それでも機能しない場合は、新しい質問をしてください。
jpyams

12

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

./script.py

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

./script.py > <filename>

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

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

したがって./script.py、実行する前に、このコマンドを実行します

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


3
./script.py> <filename> 2>&1 stderrもキャプチャする必要があります。2>&1がそれを行います
rtaft

1
@rtaftなんで?質問は具体的にはprint、ファイルの出力をパイプ処理する必要があります。stdout(スタックトレースなど)が端末に出力されることを期待するのは妥当です。
Aaron Dufour 2016

彼はそれが機能していなかった、と私のも機能していなかったと述べました。私が後で取り組んでいるこのアプリが、すべてをstderrに送信するように構成されていることを後で発見しました...理由は何ですか?
rtaft

5

Linuxを使用している場合は、teeコマンドを使用することをお勧めします。実装は次のようになります。

python python_file.py | tee any_file_name.txt

コードを変更したくない場合は、これが最善の解決策になると思います。ロガーを実装することもできますが、コードにいくつかの変更を加える必要があります。


1
すごい; 探していました
Vicrobot、

4

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

データを文字列として事前に準備し、ファイルを開いて一度にすべてを書き込むのが良い習慣だと思います。これは、入出力操作がファイルハンドルを開いている時間が長いほど、このファイルでエラー(ファイルロックエラー、I / Oエラーなど)が発生する可能性が高くなるためです。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()

1
オリジナルのディスクキャッシングパフォーマンスは許容範囲内です。ただし、このソリューションには、出力が多い場合にメモリ要件が膨らむという欠点があります。ここではおそらく何も心配する必要はありませんが、可能であればこれを回避することをお勧めします。範囲の代わりにxrange(py3範囲)を使用するのと同じアイデアなど
Gringo Suave

@Gringo:彼はこの要件を指定しませんでした。これが関係するような十分なデータをファイルに書き込むことはめったにありません。xrangeはファイルI / Oを処理しないため、これはxrangeと同じではありません。ディスクキャッシングは役立つかもしれませんが、コードボディの大部分に対してファイルハンドルを開いたままにしておくことは、依然として悪い習慣です。
憧れるマシン

1
あなたのコメントはそれ自体と矛盾しています。正直に言うと、両方のアプローチのパフォーマンスの側面は、膨大な量のデータには関係ありません。xrangeは確かに似ています。メモリ内で一度にすべてではなく、一度に1つの部分で機能します。おそらくジェネレータvsリストがより良い例です。
Gringo Suave 2011

@グリンゴ:私のコメントがどのように矛盾するかはわかりません。パフォーマンスの側面は関係ないかもしれません。ファイルハンドルを長期間開いたままにしておくと、常にエラーのリスクが高まります。ファイルのプログラミングでは、自分のプログラム内で何かを行うよりも、本質的に常にI / Oのリスクが高くなります。これは、OSを介して手を伸ばし、ファイルロックをいじる必要があるためです。コードからファイルシステムを制御しないという理由だけで、開いているファイルが短いほど良いです。xrangeはファイルI / Oとは関係がないため異なります。また、私はxrangeを使用することはほとんどありません。歓声
憧れるマシン

2
@グリンゴ:私はあなたの批判に感謝し、白熱した議論を楽しんだ。私たちはいくつかの点で反対しましたが、あなたがあなたのスタンスをとる正当な理由があることは明らかなので、私はあなたの意見を尊重します。それを合理的に終了してくれてありがとう、そしてとても良い夜を。:P
憧れるマシン

2

リダイレクトが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が必要な場合には機能しないことですが、より良い、より一般化された方法、つまり間接的なモジュールアクセスが必要です。この質問の他の回答でその実装を確認できます。


0

sys.stdoutの値を変更すると、printへのすべての呼び出しの宛先が変更されます。別の方法で印刷先を変更しても、同じ結果が得られます。

あなたのバグはどこかにあります:

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

-1

ループの印刷機能を拡張するためのもの

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

使用する必要がなく、使用while時にファイルを閉じる必要がないwith
DanielStracaboškoDec
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.