Làm cách nào để chuyển hướng đầu ra 'in' sang tệp bằng python?


183

Tôi muốn chuyển hướng in sang tệp .txt bằng python. Tôi có một vòng lặp 'for', sẽ 'in' đầu ra cho mỗi tệp .bam của tôi trong khi tôi muốn chuyển hướng TẤT CẢ các đầu ra này thành một tệp. Vì vậy, tôi đã cố gắng để đặt

 f = open('output.txt','w'); sys.stdout = f

ở đầu kịch bản của tôi. Tuy nhiên tôi không nhận được gì trong tệp .txt. Kịch bản của tôi là:

#!/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....

Vậy vấn đề là gì? Còn cách nào khác ngoài sys.stdout này không?

Tôi cần kết quả của mình như sau:

Filename: ERR001268.bam
Readlines finished!
Mean: 233
SD: 10
Interval is: (213, 252)

7
Tại sao không sử dụng f.write(data)?
Eran Zimmerman Gonen

vâng, nhưng tôi có một vài dữ liệu cho mỗi tệp bam (trung bình, SD, khoảng ...), làm cách nào tôi có thể đặt từng dữ liệu này từng cái một?
LookIntoEast

f.write(line)- nó chèn một ngắt dòng ở cuối.
Eran Zimmerman Gonen

8
@Eran Zimmerman: f.write(line)không thêm ngắt dòng vào dữ liệu.
hughdbrown

Bạn nói đúng, xấu của tôi. f.write(line+'\n')Tuy nhiên, luôn luôn có thể ..
Eran Zimmerman Gonen

Câu trả lời:


273

Cách rõ ràng nhất để làm điều này là in ra một đối tượng tệp:

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

Tuy nhiên, chuyển hướng stdout cũng làm việc cho tôi. Có thể là tốt cho một kịch bản một lần như thế này:

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

Chuyển hướng bên ngoài từ chính vỏ là một lựa chọn tốt khác:

./script.py > out.txt

Các câu hỏi khác:

Tên tệp đầu tiên trong kịch bản của bạn là gì? Tôi không thấy nó khởi tạo.

Dự đoán đầu tiên của tôi là toàn cầu không tìm thấy bất kỳ bamfiles nào, và do đó vòng lặp for không chạy. Kiểm tra xem thư mục có tồn tại không, và in ra các bamfiles trong tập lệnh của bạn.

Ngoài ra, sử dụng os.path.join và os.path.basename để thao tác các đường dẫn và tên tệp.


Dòng 8 của mã của bạn sử dụng một biến có tên tệp, nhưng nó chưa được tạo. Sau đó trong vòng lặp bạn sử dụng nó một lần nữa, nhưng không liên quan.
Gringo Suave

2
Thực hành xấu để thay đổi sys.stdout nếu bạn không cần.
máy khao khát

3
@my Tôi không tin nó là xấu cho một kịch bản đơn giản như thế này.
Gringo Suave

4
+1 Haha tốt, bạn có thể có upvote của tôi vì đó là cách làm đúng nếu bạn hoàn toàn phải làm sai cách ... Nhưng tôi vẫn nói bạn nên làm điều đó với đầu ra tệp thông thường.
máy khao khát

1
Làm thế nào để chuyển hướng và in đầu ra trên bàn điều khiển? Có vẻ như "print ()" trong Python không thể hiển thị khi stdrr được chuyển hướng?
exteral

70

Bạn có thể chuyển hướng in với >>toán tử.

f = open(filename,'w')
print >>f, 'whatever'     # Python 2.x
print('whatever', file=f) # Python 3.x

Trong hầu hết các trường hợp, tốt hơn hết là bạn chỉ nên ghi vào tệp một cách bình thường.

f.write('whatever')

hoặc, nếu bạn có một vài mục bạn muốn viết với khoảng trắng ở giữa, như print:

f.write(' '.join(('whatever', str(var2), 'etc')))

2
Nếu có nhiều câu lệnh đầu ra, chúng có thể bị cũ nhanh. Các áp phích ý tưởng ban đầu là hợp lệ; có một cái gì đó sai với kịch bản.
Gringo Suave

1
Ý tưởng ban đầu của Poster là hoàn toàn không hợp lệ. Không có lý do để chuyển hướng thiết bị xuất chuẩn ở đây, vì anh ta đã lấy dữ liệu thành một biến.
máy khao khát

Tôi nghĩ rằng anh ấy có nghĩa là "hợp lệ về mặt kỹ thuật", trong thực tế, bạn có thể chuyển hướng sys.stdout, không phải đó là một ý tưởng tốt.
agf

35

Tham chiếu API Python 2 hoặc Python 3 :

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

Đối số tệp phải là một đối tượng với một write(string)phương thức; nếu nó không có mặt hoặc None, sys.stdoutsẽ được sử dụng. Vì các đối số được in được chuyển đổi thành chuỗi văn bản, print()không thể được sử dụng với các đối tượng tệp chế độ nhị phân. Đối với những điều này, sử dụng file.write(...)thay thế.

đối tượng tệp thường chứa write()phương thức, tất cả những gì bạn cần làm là truyền đối tượng tệp vào đối số của nó.

Viết / ghi đè lên tệp

with open('file.txt', 'w') as f:
    print('hello world', file=f)

Viết / Nối vào tập tin

with open('file.txt', 'a') as f:
    print('hello world', file=f)

2
Tôi chỉ bối rối tại sao một số câu trả lời trước đó là để khỉ vá toàn cầu sys.stdout:(
Yeo

35

Điều này hoạt động hoàn hảo:

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

Bây giờ lời chào sẽ được ghi vào tệp test.txt. Đảm bảo đóng stdoutbằng a close, nếu không có nó, nội dung sẽ không được lưu trong tệp


3
nhưng ngay cả khi chúng tôi thực hiện sys.stdout.close(), nếu bạn nhập bất cứ thứ gì vào shell python, nó sẽ hiển thị lỗi là ValueError: I/O operation on closed file. imgur.com/a/xby9P . Cách tốt nhất để xử lý vấn đề này là làm theo những gì @Gringo Suave đã đăng
Mourya

24

Không sử dụng print, sử dụnglogging

Bạn có thể thay đổi sys.stdoutđể trỏ đến một tập tin, nhưng đây là một cách khá khó hiểu và không linh hoạt để xử lý vấn đề này. Thay vì sử dụng print, hãy sử dụng loggingmô-đun.

Với logging, bạn có thể in giống như bạn muốn stdouthoặc bạn cũng có thể ghi đầu ra vào một tệp. Bạn thậm chí có thể sử dụng các mức thông điệp khác nhau ( critical, error, warning, info, debug), ví dụ, chỉ in những vấn đề lớn đối với giao diện điều khiển, nhưng vẫn đăng nhập hành động mã nhỏ vào một tập tin.

Một ví dụ đơn giản

Nhập logging, nhận loggervà đặt mức xử lý:

import logging
logger = logging.getLogger()
logger.setLevel(logging.DEBUG) # process everything, even if everything isn't printed

Nếu bạn muốn in ra thiết bị xuất chuẩn:

ch = logging.StreamHandler()
ch.setLevel(logging.INFO) # or any other level
logger.addHandler(ch)

Nếu bạn cũng muốn ghi vào một tệp (nếu bạn chỉ muốn ghi vào một tệp, hãy bỏ qua phần cuối cùng):

fh = logging.FileHandler('myLog.log')
fh.setLevel(logging.DEBUG) # or any level you want
logger.addHandler(fh)

Sau đó, bất cứ nơi nào bạn sẽ sử printdụng một trong các loggerphương pháp:

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

Để tìm hiểu thêm về việc sử dụng các loggingtính năng nâng cao hơn , hãy đọc logginghướng dẫn tuyệt vời trong tài liệu Python .


Xin chào, tôi muốn sử dụng nhật ký này để ghi dữ liệu bảng điều khiển vào tệp nhật ký với thời gian như lúc dữ liệu được lấy. Nhưng tôi không thể hiểu chức năng đăng nhập hoặc thư viện đúng cách. Bạn có thể giúp tôi với điều này
haris

@haris Đọc qua hướng dẫn ghi nhật ký của tài liệu Python và xem các ví dụ trong các câu hỏi khác về Stack Overflow (có rất nhiều trong số chúng). Nếu bạn vẫn không thể làm cho nó hoạt động, hãy hỏi một câu hỏi mới.
jpyams

12

Giải pháp đơn giản nhất không phải là thông qua python; nó xuyên qua vỏ. Từ dòng đầu tiên của tệp của bạn ( #!/usr/bin/python) Tôi đoán bạn đang sử dụng hệ thống UNIX. Chỉ sử dụng các printcâu lệnh như bạn thường làm và không mở tệp trong tập lệnh của bạn. Khi bạn chạy tệp, thay vì

./script.py

để chạy tập tin, sử dụng

./script.py > <filename>

nơi bạn thay thế <filename>bằng tên của tệp bạn muốn đầu ra đi vào. Mã >thông báo cho (hầu hết) các vỏ để đặt thiết bị xuất chuẩn vào tệp được mô tả bởi mã thông báo sau.

Một điều quan trọng cần được đề cập ở đây là "script.py" cần phải được thực thi ./script.pyđể chạy.

Vì vậy, trước khi chạy ./script.py, thực hiện lệnh này

chmod a+x script.py (làm cho tập lệnh thực thi được cho tất cả người dùng)


3
./script.py> <tên tệp> 2> & 1 Bạn cũng cần chụp stderr. 2> & 1 sẽ làm điều đó
rtaft

1
@rtaft Tại sao? Câu hỏi đặc biệt muốn dẫn đầu ra của printmột tập tin. Sẽ là hợp lý khi mong đợi thiết bị xuất chuẩn (dấu vết ngăn xếp và tương tự) vẫn được in ra thiết bị đầu cuối.
Aaron Dufour

Anh ta nói nó không hoạt động, tôi cũng không làm việc. Sau đó tôi phát hiện ra rằng ứng dụng này tôi đang làm việc được cấu hình để hướng mọi thứ đến stderr ... idk tại sao.
rtaft

5

Nếu bạn đang sử dụng Linux, tôi khuyên bạn nên sử dụng teelệnh. Việc thực hiện diễn ra như sau:

python python_file.py | tee any_file_name.txt

Nếu bạn không muốn thay đổi bất cứ điều gì trong mã, tôi nghĩ rằng đây có thể là giải pháp tốt nhất có thể. Bạn cũng có thể thực hiện logger nhưng bạn cần thực hiện một số thay đổi trong mã.


1
tuyệt quá; đang tìm kiếm nó
Vicrobot

4

Bạn có thể không thích câu trả lời này, nhưng tôi nghĩ đó là QUYỀN. Đừng thay đổi điểm xuất phát của bạn trừ khi thực sự cần thiết (có thể bạn đang sử dụng thư viện chỉ xuất ra thiết bị xuất chuẩn ??? rõ ràng không phải là trường hợp ở đây).

Tôi nghĩ như một thói quen tốt, bạn nên chuẩn bị dữ liệu trước thời hạn dưới dạng chuỗi, sau đó mở tệp của bạn và viết toàn bộ nội dung cùng một lúc. Điều này là do các hoạt động đầu vào / đầu ra là thời gian bạn mở tệp xử lý càng lâu, càng có nhiều khả năng xảy ra lỗi với tệp này (lỗi khóa tệp, lỗi i / o, v.v.). Chỉ cần thực hiện tất cả trong một thao tác sẽ không có câu hỏi khi nào nó có thể bị lỗi.

Đây là một ví dụ:

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

Và sau đó khi bạn hoàn tất việc thu thập "dòng dữ liệu" của mình cho một dòng trên mỗi mục danh sách, bạn có thể kết hợp chúng với một số '\n'ký tự để làm cho toàn bộ nội dung có thể xuất ra; thậm chí có thể bọc câu lệnh đầu ra của bạn trong một withkhối, để đảm bảo an toàn hơn (sẽ tự động đóng tay cầm đầu ra của bạn ngay cả khi có sự cố):

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!!!"

Tuy nhiên nếu bạn có nhiều dữ liệu để viết, bạn có thể viết từng phần một. Tôi không nghĩ nó có liên quan đến ứng dụng của bạn nhưng đây là lựa chọn thay thế:

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
Với hiệu suất bộ nhớ đệm đĩa của bản gốc nên được chấp nhận. Tuy nhiên, giải pháp này có nhược điểm là đáp ứng yêu cầu bộ nhớ nếu có nhiều đầu ra. Mặc dù có lẽ không có gì phải lo lắng ở đây, nhưng nói chung nên tránh điều này nếu có thể. Ý tưởng tương tự như sử dụng xrange (phạm vi py3) thay vì phạm vi, v.v.
Gringo Suave

@Gringo: Anh ấy không chỉ định yêu cầu này. Hiếm khi tôi viết đủ dữ liệu vào một tệp mà điều này có liên quan. Đây không phải là ý tưởng tương tự như xrange vì xrange không xử lý tệp i / o. Bộ nhớ đệm trên đĩa có thể giúp ích nhưng vẫn là một cách thực hành tồi để giữ một tệp xử lý mở cho một khối mã lớn.
máy khao khát

1
Nhận xét của bạn mâu thuẫn với chính nó. Thành thật mà nói, khía cạnh hiệu suất của cả hai phương pháp đều không liên quan đến lượng dữ liệu không lớn. xrange chắc chắn là tương tự, nó hoạt động trên một mảnh tại một thời điểm thay vì tất cả cùng một lúc trong bộ nhớ. Có lẽ một máy phát điện vs danh sách là một ví dụ tốt hơn mặc dù.
Gringo Suave

@Gringo: Tôi không thấy nhận xét của mình mâu thuẫn như thế nào. Có thể khía cạnh hiệu suất không liên quan, giữ cho tệp xử lý mở trong thời gian dài luôn làm tăng nguy cơ lỗi. Trong tập tin lập trình, i / o luôn tiềm ẩn nhiều rủi ro hơn so với làm một việc gì đó trong chương trình của riêng bạn, bởi vì điều đó có nghĩa là bạn phải tiếp cận với hệ điều hành và loay hoay với việc khóa tập tin. Bạn mở tệp càng ngắn thì càng tốt, đơn giản vì bạn không kiểm soát hệ thống tệp từ mã của mình. xrange thì khác vì nó không liên quan gì đến tập tin i / o và FYI tôi cũng hiếm khi sử dụng xrange; chúc mừng
máy khao khát

2
@Gringo: Tôi đánh giá cao sự chỉ trích của bạn và rất thích cuộc tranh luận sôi nổi. Mặc dù chúng tôi không đồng ý ở một số điểm, tôi vẫn tôn trọng quan điểm của bạn vì rõ ràng bạn có lý do chính đáng để đưa ra lập trường của mình. Cảm ơn vì đã kết thúc nó một cách hợp lý và có một đêm rất tốt. : P
máy khao khát

2

Nếu chuyển hướng stdouthoạt động cho vấn đề của bạn, câu trả lời của Gringo Suave là một minh chứng tốt cho cách thực hiện.

Để làm cho nó thậm chí còn dễ dàng hơn , tôi đã thực hiện một phiên bản sử dụng contextmanagers cho một cú pháp gọi khái quát ngắn gọn bằng cách sử dụng withtuyên bố:

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

Để sử dụng nó, bạn chỉ cần làm như sau (xuất phát từ ví dụ của Suave):

with open('out.txt', 'w') as outfile:
    with redirected_stdout(outfile):
        for i in range(2):
            print('i =', i)

Nó hữu ích cho việc chuyển hướng có chọn lọc printkhi một mô-đun sử dụng nó theo cách bạn không thích. Nhược điểm duy nhất (và đây là công cụ giải quyết trong nhiều tình huống) là nó không hoạt động nếu một người muốn có nhiều luồng với các giá trị khác nhau stdout, nhưng điều đó đòi hỏi một phương pháp tốt hơn, tổng quát hơn: truy cập mô-đun gián tiếp. Bạn có thể thấy việc thực hiện điều đó trong các câu trả lời khác cho câu hỏi này.


0

Thay đổi giá trị của sys.stdout sẽ thay đổi đích của tất cả các cuộc gọi để in. Nếu bạn sử dụng một cách khác để thay đổi đích in, bạn sẽ nhận được kết quả tương tự.

Lỗi của bạn là ở một nơi khác:

  • nó có thể nằm trong mã bạn đã xóa cho câu hỏi của mình (tên tệp đến từ đâu để gọi mở?)
  • cũng có thể là bạn không đợi dữ liệu bị xóa: nếu bạn in trên thiết bị đầu cuối, dữ liệu sẽ bị xóa sau mỗi dòng mới, nhưng nếu bạn in ra một tệp, nó chỉ bị xóa khi bộ đệm tiêu chuẩn đầy (4096 byte trên hầu hết các hệ thống).

-1

Một cái gì đó để mở rộng chức năng in cho các vòng lặp

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

không cần sử dụng whilevà không cần phải đóng tệp khi sử dụngwith
Daniel Stracaboško
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.