파이썬을 사용하여 '인쇄'출력을 파일로 리디렉션하는 방법은 무엇입니까?


184

파이썬을 사용하여 인쇄를 .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)

7
왜 사용하지 f.write(data)않습니까?
Eran Zimmerman Gonen

예, 그러나 각 bam 파일 (평균, SD, 간격 ...)에 대해 여러 데이터가 있습니다.이 데이터를 하나씩 어떻게 넣을 수 있습니까?
LookIntoEast

f.write(line)-끝에 줄 바꿈을 삽입합니다.
Eran Zimmerman Gonen

8
@Eran Zimmerman : f.write(line)데이터에 줄 바꿈을 추가하지 않습니다.
hughdbrown

당신 말이 맞아요 f.write(line+'\n')그러나 항상 ..
Eran Zimmerman Gonen

답변:


274

이를 수행하는 가장 확실한 방법은 파일 객체로 인쇄하는 것입니다.

with open('out.txt', 'w') as f:
    print >> f, 'Filename:', filename     # Python 2.x
    print('Filename:', filename, file=f)  # Python 3.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()

쉘 자체에서 외부로 리디렉션하는 것도 좋은 방법입니다.

./script.py > out.txt

다른 질문:

스크립트에서 첫 번째 파일 이름은 무엇입니까? 초기화되지 않았습니다.

내 첫 번째 추측은 glob이 bamfile을 찾지 못하므로 for 루프가 실행되지 않는다는 것입니다. 폴더가 있는지 확인하고 스크립트에서 bamfile을 인쇄하십시오.

또한 os.path.join 및 os.path.basename 을 사용하여 경로 및 파일 이름을 조작하십시오.


코드의 8 행은 filename이라는 변수를 사용하지만 아직 생성되지 않았습니다. 나중에 루프에서 다시 사용하지만 관련성이 없습니다.
Gringo Suave

2
필요하지 않은 경우 sys.stdout을 변경하는 나쁜 습관입니다.
기계를 갈망하는 기계

3
@my 나는 이것과 같은 간단한 스크립트가 나쁘다는 것을 확신하지 못한다.
Gringo Suave

4
+1 Haha는 당신이 절대적으로 잘못된 방식으로해야한다면 그것을하는 올바른 방법이기 때문에 나의 의견을 밝힐 수 있습니다. 그러나 나는 여전히 정기적 인 파일 출력으로해야한다고 말합니다.
기계를 갈망하는 기계

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

1
포스터의 원래 아이디어는 절대적으로 유효하지 않습니다. 그는 이미 데이터를 변수로 가져 오기 때문에 stdout을 리디렉션 할 이유가 없습니다.
기계 열망

나는 그가 "기술적으로 유효하다"는 것을 의미한다고 생각한다. 실제로 당신은 sys.stdout그것이 좋은 생각이 아니라 방향 전환을 할 수 있다는 것이다.
agf

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

이제 hello가 test.txt 파일에 작성됩니다. 를 닫 확인 stdout로모그래퍼 close파일에 저장되지 않습니다없이, 컨텐츠를


3
그러나 우리가 수행하더라도 sys.stdout.close()파이썬 쉘에 아무것도 입력하면 오류가 ValueError: I/O operation on closed file. imgur.com/a/xby9P 로 표시됩니다 . 이를 처리하는 가장 좋은 방법은 @Gringo Suave가 게시 한 내용을 따르는 것입니다.
Mourya

24

사용하지 마십시오 print, 사용logging

sys.stdout파일을 가리 키도록 변경할 수는 있지만,이 문제를 처리하는 것은 매우 어수선하고 유연하지 않은 방법입니다. 을 사용하는 대신 모듈을 print사용하십시오 logging.

을 사용 logging하여 원하는대로 인쇄 stdout하거나 출력을 파일에 쓸 수도 있습니다. 당신은 (다른 메시지 수준을 사용할 수 있습니다 critical, error, warning, info, debug), 예를 들어, 콘솔 만에 주요 이슈를 인쇄 할 수 있지만, 여전히 파일에 마이너 코드의 작업을 기록합니다.

간단한 예

Import를 가져 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기능을 사용하는 방법에 대한 자세한 내용은 loggingPython 문서에서 훌륭한 자습서를 읽으십시오 .


안녕하세요,이 로깅을 사용하여 데이터가 수집되는 시간과 같은 시간으로 콘솔 데이터를 로그 파일에 기록하려고합니다. 그러나 로깅 기능이나 라이브러리를 올바르게 이해할 수 없습니다. 이것 좀 도와 줄 수 있어요
haris

@haris 파이썬 문서의 로깅 튜토리얼을 읽고 스택 오버플로에 대한 다른 질문의 예제를 확인하십시오 (많은 것들이 있습니다). 그래도 작동하지 않으면 새로운 질문을하십시오.
jpyams

12

가장 쉬운 해결책은 파이썬을 통한 것이 아닙니다. 껍질을 통해. 파일의 첫 번째 줄 ( #!/usr/bin/python)에서 유닉스 시스템을 사용하고 있다고 생각합니다. print평소처럼 문장을 사용 하고 스크립트에서 파일을 전혀 열지 마십시오. 파일을 실행할 때 대신

./script.py

파일을 실행하려면

./script.py > <filename>

여기서 <filename>출력을 넣을 파일 이름으로 바꿉니다. >토큰은 다음과 같은 토큰에 의해 기술 된 파일에 설정된 표준 출력으로 (대부분의) 껍질을 알려줍니다.

여기서 언급해야 할 중요한 사항 중 하나는 "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

그는 그것이 작동하지 않는다고 말했고, 내 것도 작동하지 않았다. 나는 나중에 작업중인이 응용 프로그램이 모든 것을 stderr ... idk 이유로 보내도록 구성되었음을 발견했습니다.
rtaft

5

Linux를 사용하는 경우 tee명령 을 사용하는 것이 좋습니다 . 구현은 다음과 같습니다.

python python_file.py | tee any_file_name.txt

코드에서 아무것도 바꾸고 싶지 않다면 이것이 최선의 해결책이라고 생각합니다. 로거를 구현할 수도 있지만 코드를 약간 변경해야합니다.


1
큰; 그것을 찾고 있었다
Vicrobot

4

이 답변이 마음에 들지 않을 수도 있지만 정답이라고 생각합니다. 반드시 필요한 경우가 아니면 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()

1
원본의 디스크 캐싱 성능은 허용 가능해야합니다. 그러나이 솔루션은 많은 출력이있는 경우 메모리 요구 사항을 확장시키는 단점이 있습니다. 여기서는 걱정할 것이 없지만 일반적으로 가능한 경우 이것을 피하는 것이 좋습니다. 범위 대신 xrange (py3 범위)를 사용하는 것과 같은 아이디어입니다.
Gringo Suave

@Gringo : 그는이 요구 사항을 지정하지 않았습니다. 거의 관련이없는 파일에 충분한 데이터를 쓰지 않습니다. xrange는 파일 i / o를 처리하지 않기 때문에 xrange와 같은 개념이 아닙니다. 디스크 캐싱이 도움이 될 수 있지만 대량의 코드를 위해 파일 핸들을 열어 두는 것은 여전히 ​​나쁜 습관입니다.
기계 갈망

1
귀하의 의견은 모순됩니다. 솔직히 말해서 두 가지 접근 방식의 성능 측면은 엄청난 양의 데이터와 관련이 없습니다. xrange는 확실히 비슷합니다. 메모리에서 한 번에 한 번에 한 번에 작동하지 않습니다. 아마도 발전기 대 목록이 더 좋은 예일 것입니다.
Gringo Suave

@Gringo : 내 의견이 어떻게 모순되는지 알 수 없습니다. 성능 측면과 관련이 없을 수 있습니다. 파일 핸들을 오랫동안 열어두면 항상 오류 위험이 높아집니다. 프로그래밍에서 파일 I / O는 본질적으로 자신의 프로그램 내에서 무언가를 수행하는 것보다 본질적으로 더 위험합니다. 왜냐하면 OS를 통해 접근하여 파일 잠금으로 혼란을 겪기 때문입니다. 파일을 짧게 열수록 파일 시스템을 코드에서 제어하지 않기 때문에 더 좋습니다. xrange는 파일 i / o와 관련이 없기 때문에 다르며, 참고로 xrange를 거의 사용하지 않습니다. 건배
기계 갈망

2
@Gringo : 당신의 비판에 감사하고 열띤 토론을 즐겼습니다. 비록 우리가 어떤 견해에 동의하지 않더라도, 나는 당신이 당신의 입장을 취할 충분한 이유가 있다는 것이 분명하기 때문에 당신의 견해를 여전히 존중합니다. 합리적으로 끝내 주셔서 감사합니다 그리고 아주 좋은 밤을 보내십시오. : 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 값을 변경하면 인쇄 할 모든 호출의 대상이 변경됩니다. 다른 방법으로 인쇄 대상을 변경하면 동일한 결과가 나타납니다.

당신의 버그는 다른 곳에 있습니다 :

  • 질문을 위해 제거한 코드에있을 수 있습니다 (호출을 열려면 파일 이름이 어디에서 왔습니까?)
  • 데이터가 플러시되기를 기다리지 않을 수도 있습니다. 터미널에 인쇄하면 매 줄마다 데이터가 플러시되지만 파일로 인쇄하면 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
Daniel Stracaboško
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.