লেজুর মতো পাইথন সহ কোনও ফাইলের শেষ এন লাইন পান


181

আমি একটি ওয়েব অ্যাপ্লিকেশনের জন্য একটি লগ ফাইল ভিউয়ার লিখছি এবং তার জন্য আমি লগ ফাইলের লাইনগুলিতে প্যাগিনেট করতে চাই। ফাইলের আইটেমগুলি নীচে থাকা নতুন আইটেমটির সাথে ভিত্তি করে লাইন।

সুতরাং আমার একটি tail()পদ্ধতি প্রয়োজন nযা নীচ থেকে লাইনগুলি পড়তে পারে এবং একটি অফসেট সমর্থন করে। যা নিয়ে এসেছি তা দেখতে এরকম দেখাচ্ছে:

def tail(f, n, offset=0):
    """Reads a n lines from f with an offset of offset lines."""
    avg_line_length = 74
    to_read = n + offset
    while 1:
        try:
            f.seek(-(avg_line_length * to_read), 2)
        except IOError:
            # woops.  apparently file is smaller than what we want
            # to step back, go to the beginning instead
            f.seek(0)
        pos = f.tell()
        lines = f.read().splitlines()
        if len(lines) >= to_read or pos == 0:
            return lines[-to_read:offset and -offset or None]
        avg_line_length *= 1.3

এটি কি যুক্তিসঙ্গত পন্থা? অফসেটের সাহায্যে লগ ফাইলগুলি টেল করার প্রস্তাবিত উপায় কী?


আমার সিস্টেমে (লিনাক্স এসএলইএস 10), শেষের সাথে সম্পর্কিত সন্ধান করা একটি আইওইরার উত্থাপন করে "নোনজারো শেষ-আপেক্ষিক সন্ধান করতে পারে না"। আমি এই সমাধানটি পছন্দ করি তবে ফাইলের দৈর্ঘ্য ( seek(0,2)তখন tell()) পাওয়ার জন্য এটি সংশোধন করেছি এবং শুরুর তুলনায় সেই মানটি ব্যবহার করি।
অ্যান

2
অভিনন্দন - এই প্রশ্নটি এটিকে কিপ্পো উত্স কোড হিসাবে তৈরি করেছে
মাইলস

পরামিতি openজেনারেট করতে ব্যবহৃত কমান্ড fফাইল বস্তুর নির্ধারণ করা উচিত, কারণ নির্ভর করে যদি f=open(..., 'rb')বা ভিন্নভাবে প্রক্রিয়া করা উচিত নয়f=open(..., 'rt')f
ইগর Fobia

উত্তর:


123

এটি আপনার চেয়ে দ্রুত হতে পারে। লাইনের দৈর্ঘ্য সম্পর্কে কোনও অনুমান করে না। যতক্ষণ না এটি 'characters n' অক্ষরের সঠিক সংখ্যা খুঁজে পাওয়া যায় ততক্ষণে একবারে ফাইলটিকে এক ব্লক দিয়ে ব্যাক করে।

def tail( f, lines=20 ):
    total_lines_wanted = lines

    BLOCK_SIZE = 1024
    f.seek(0, 2)
    block_end_byte = f.tell()
    lines_to_go = total_lines_wanted
    block_number = -1
    blocks = [] # blocks of size BLOCK_SIZE, in reverse order starting
                # from the end of the file
    while lines_to_go > 0 and block_end_byte > 0:
        if (block_end_byte - BLOCK_SIZE > 0):
            # read the last block we haven't yet read
            f.seek(block_number*BLOCK_SIZE, 2)
            blocks.append(f.read(BLOCK_SIZE))
        else:
            # file too small, start from begining
            f.seek(0,0)
            # only read what was not read
            blocks.append(f.read(block_end_byte))
        lines_found = blocks[-1].count('\n')
        lines_to_go -= lines_found
        block_end_byte -= BLOCK_SIZE
        block_number -= 1
    all_read_text = ''.join(reversed(blocks))
    return '\n'.join(all_read_text.splitlines()[-total_lines_wanted:])

আমি যখন লাইন দৈর্ঘ্যের সম্পর্কে কৌতুক অনুমান পছন্দ করি না - যখন ব্যবহারিক বিষয় হিসাবে - আপনি কখনই এর মতো জিনিস জানতে পারবেন না।

সাধারণত, এটি লুপের মাধ্যমে প্রথম বা দ্বিতীয় পাসের শেষ 20 টি লাইন সনাক্ত করবে। যদি আপনার character৪ টি চরিত্রের জিনিসটি সত্য হয় তবে আপনি ব্লকটির আকার 2048 করুন এবং আপনি প্রায় অবিলম্বে 20 লাইনগুলি লেজ করুন।

এছাড়াও, আমি শারীরিক ওএস ব্লকগুলির সাথে সূক্ষ্ম প্রান্তিককরণের চেষ্টা করে অনেক মস্তিষ্কের ক্যালোরি বার করি না। এই উচ্চ-স্তরের আই / ও প্যাকেজগুলি ব্যবহার করে, আমি সন্দেহ করি যে আপনি ওএস ব্লক সীমানায় সারিবদ্ধ করার চেষ্টা করার কোনও কার্যকারিতা ফলাফল দেখতে পাবেন। আপনি যদি নিম্ন-স্তরের I / O ব্যবহার করেন তবে আপনি একটি দ্রুতগতি দেখতে পাবেন।


হালনাগাদ

পাইথন ৩.২ এবং তার বেশি বয়সের জন্য, টেক্সট ফাইলগুলির মতো বাইটগুলি প্রক্রিয়াটি অনুসরণ করুন ( মোড স্ট্রিংয়ে "বি" ছাড়াই খোলা ), কেবল ফাইলের প্রারম্ভের সাথে সম্পর্কিত সন্ধান করা হবে (ব্যতিক্রমটি একেবারে ফাইলের শেষের দিকে খুঁজছেন) অনুসন্ধান (0, 2)) সহ:

উদাহরণ: f = open('C:/.../../apache_logs.txt', 'rb')

 def tail(f, lines=20):
    total_lines_wanted = lines

    BLOCK_SIZE = 1024
    f.seek(0, 2)
    block_end_byte = f.tell()
    lines_to_go = total_lines_wanted
    block_number = -1
    blocks = []
    while lines_to_go > 0 and block_end_byte > 0:
        if (block_end_byte - BLOCK_SIZE > 0):
            f.seek(block_number*BLOCK_SIZE, 2)
            blocks.append(f.read(BLOCK_SIZE))
        else:
            f.seek(0,0)
            blocks.append(f.read(block_end_byte))
        lines_found = blocks[-1].count(b'\n')
        lines_to_go -= lines_found
        block_end_byte -= BLOCK_SIZE
        block_number -= 1
    all_read_text = b''.join(reversed(blocks))
    return b'\n'.join(all_read_text.splitlines()[-total_lines_wanted:])

13
এটি ছোট লগফাইলে ব্যর্থ হয় - আইওআরআর: অবৈধ যুক্তি - f.seek (ব্লক * 1024, 2)
ওহনেস

1
সত্যিই খুব সুন্দর পদ্ধতির। আমি উপরের কোডটির
জিম্পাওলো রডোলো

6
অজগর 3.2 এ আর কাজ করে না। আমি পেয়েছি আমি io.UnsupportedOperation: can't do nonzero end-relative seeksঅফসেটটি 0 তে পরিবর্তন করতে পারি তবে এটি ফাংশনের উদ্দেশ্যকে পরাস্ত করে।
যৌক্তিক মিথ্যাচার 21

4
@ ডেভিডইংলুন্ড কারণ এখানে রয়েছে । সংক্ষেপে: ফাইলের শেষের তুলনায় অনুসন্ধানের জন্য পাঠ্য মোডে অনুমতি দেওয়া হয় না কারণ সম্ভবত ফাইলের বিষয়বস্তুগুলি ডিকোড করতে হয় এবং সাধারণভাবে এনকোডড বাইটের ক্রমগুলির মধ্যে একটি স্বেচ্ছাসেবক অবস্থানের সন্ধান করা যখন আপনি অনির্ধারিত ফলাফল পেতে পারেন সেই অবস্থান থেকে শুরু করে ইউনিকোডে ডিকোড করার চেষ্টা করুন। লিঙ্কে দেওয়া পরামর্শটি হ'ল ফাইলটি বাইনারি মোডে খোলার চেষ্টা করুন এবং নিজেই ডিকোডিং করুন, ডিকোডেরর ব্যতিক্রমগুলি ধরা।
সর্বোচ্চ

6
এই কোডটি ব্যবহার করবেন না। এটি অজগর ২.7-তে কিছু সীমান্ত ক্ষেত্রে লাইনগুলি দূষিত করে। নীচে @ পেপারক্রেনের উত্তর এটি ঠিক করে দিয়েছে।
এক্স এপল

88

পাইথন 2 এ ইউনিক্স-এর মতো একটি সিস্টেম ধরে নেওয়া যায়:

import os
def tail(f, n, offset=0):
  stdin,stdout = os.popen2("tail -n "+n+offset+" "+f)
  stdin.close()
  lines = stdout.readlines(); stdout.close()
  return lines[:,-offset]

অজগর 3 এর জন্য আপনি এটি করতে পারেন:

import subprocess
def tail(f, n, offset=0):
    proc = subprocess.Popen(['tail', '-n', n + offset, f], stdout=subprocess.PIPE)
    lines = proc.stdout.readlines()
    return lines[:, -offset]

5
প্ল্যাটফর্ম স্বাধীন হতে হবে। তদ্ব্যতীত, আপনি যদি প্রশ্নটি পড়েন তবে আপনি দেখতে পাবেন যে চ একটি অবজেক্টের মতো একটি ফাইল।
আর্মিন রোনাচার

40
প্রশ্নটি প্ল্যাটফর্ম নির্ভরতা গ্রহণযোগ্য নয় বলে দেয় না। আমি এটি দেখতে ব্যর্থ হয়েছি কেন যখন এটি খুব ইউনিক্স সরবরাহ করে (যখন আপনি যা খুঁজছিলেন তা হতে পারে ... অবশ্যই আমার জন্য ছিল) প্রশ্নটি যা জিজ্ঞাসা করে ঠিক তা করার পদ্ধতিটি কেন v
শাবিরোবে

3
ধন্যবাদ, আমি ভাবছিলাম আমাকে এটি খাঁটি পাইথনে সমাধান করতে হবে তবে ইউনিক্স ইউটিলিটিগুলি যখন হাতের কাছে থাকবে তখন তাদের ব্যবহার না করার কোনও কারণ নেই, তাই আমি এটি নিয়ে গেলাম। আধুনিক পাইথনে এফডব্লিউআইডাব্লু, সাবপ্রসেসি.সিএক_আউটপুট সম্ভবত ওএসপিপেন 2 এর চেয়ে বেশি পছন্দনীয়; এটি জিনিসগুলিকে কিছুটা সহজতর করে কারণ এটি কেবল স্ট্রিং হিসাবে আউটপুট দেয় এবং একটি শূন্য-বহির্গমন কোডে উত্থাপন করে।
মুরুনি

3
যদিও এটি প্ল্যাটফর্ম নির্ভর, এটি যা যা চাওয়া হয়েছে তা করার একটি অত্যন্ত কার্যকর উপায়, পাশাপাশি এটি করার একটি অত্যন্ত দ্রুত উপায় (আপনাকে পুরো ফাইলটি মেমোরিতে লোড করতে হবে না)। @ শ্যাববাইরোব
আর্থলোন

6
আপনি অফসেটের মতো offset_total = str(n+offset)stdin,stdout = os.popen2("tail -n "+offset_total+" "+f)TypeErrors (cannot concatenate int+str)
প্রাক

32

আমার উত্তর এখানে। খাঁটি অজগর। টাইমিট ব্যবহার করা এটি বেশ দ্রুত বলে মনে হচ্ছে। একটি লগ ফাইলের 100 টি লাইনে 100,000 লাইন রয়েছে:

>>> timeit.timeit('tail.tail(f, 100, 4098)', 'import tail; f = open("log.txt", "r");', number=10)
0.0014600753784179688
>>> timeit.timeit('tail.tail(f, 100, 4098)', 'import tail; f = open("log.txt", "r");', number=100)
0.00899195671081543
>>> timeit.timeit('tail.tail(f, 100, 4098)', 'import tail; f = open("log.txt", "r");', number=1000)
0.05842900276184082
>>> timeit.timeit('tail.tail(f, 100, 4098)', 'import tail; f = open("log.txt", "r");', number=10000)
0.5394978523254395
>>> timeit.timeit('tail.tail(f, 100, 4098)', 'import tail; f = open("log.txt", "r");', number=100000)
5.377126932144165

কোডটি এখানে:

import os


def tail(f, lines=1, _buffer=4098):
    """Tail a file and get X lines from the end"""
    # place holder for the lines found
    lines_found = []

    # block counter will be multiplied by buffer
    # to get the block size from the end
    block_counter = -1

    # loop until we find X lines
    while len(lines_found) < lines:
        try:
            f.seek(block_counter * _buffer, os.SEEK_END)
        except IOError:  # either file is too small, or too many lines requested
            f.seek(0)
            lines_found = f.readlines()
            break

        lines_found = f.readlines()

        # we found enough lines, get out
        # Removed this line because it was redundant the while will catch
        # it, I left it for history
        # if len(lines_found) > lines:
        #    break

        # decrement the block counter to get the
        # next X bytes
        block_counter -= 1

    return lines_found[-lines:]

3
মার্জিত সমাধান! কি if len(lines_found) > lines:সত্যিই প্রয়োজনীয়? শর্তটিও কি তা loopধরবে না?
ম্যাক্সিমিলিয়ান পিটার্স

আমার বোঝার জন্য একটি প্রশ্ন: os.SEEK_ENDকেবল স্পষ্টতার জন্য ব্যবহার করা হয়? আমি যতদূর খুঁজে পেয়েছি, এর মান ধ্রুবক (= 2)। আমি এটি ছেড়ে যেতে সক্ষম হতে এটি ছেড়ে দেওয়ার সম্পর্কে ভাবছিলাম import os। দুর্দান্ত সমাধানের জন্য ধন্যবাদ!
n1k31t4

2
@ ম্যাক্সিমিলিয়ান পিটারস হ্যাঁ এটি প্রয়োজন হয় না. আমি এটা মন্তব্য।
গ্লেনবট

@ ডেক্সটারমোরগান আপনি os.SEEK_ENDএর পূর্ণসংখ্যার সমতুল্য সাথে প্রতিস্থাপন করতে পারেন । এটি মূলত পাঠযোগ্যতার জন্য ছিল।
glenbot

1
আমি upvated, কিন্তু একটি ছোট কুলুঙ্গি আছে। চাইতে পর প্রথম লাইন পঠিত অসম্পূর্ণ হতে পারে, তাই এন _complete_lines আমি পরিবর্তন পেতে while len(lines_found) < linesথেকে while len(lines_found) <= linesআমার কপিতে। ধন্যবাদ!
গ্রাহাম ক্লিন 11

30

পুরো ফাইলটি যদি গ্রহণযোগ্য হয় তবে একটি ডেক ব্যবহার করুন।

from collections import deque
deque(f, maxlen=n)

২.6 এর আগে ডেকের কাছে ম্যাক্সলেন বিকল্প ছিল না তবে এটি কার্যকর করা যথেষ্ট সহজ।

import itertools
def maxque(items, size):
    items = iter(items)
    q = deque(itertools.islice(items, size))
    for item in items:
        del q[0]
        q.append(item)
    return q

যদি শেষ থেকে ফাইলটি পড়ার প্রয়োজন হয় তবে গ্যালাপ (ওরফে এক্সফোনশিয়াল) অনুসন্ধানটি ব্যবহার করুন।

def tail(f, n):
    assert n >= 0
    pos, lines = n+1, []
    while len(lines) <= n:
        try:
            f.seek(-pos, 2)
        except IOError:
            f.seek(0)
            break
        finally:
            lines = list(f)
        pos *= 2
    return lines[-n:]

কেন যে নীচে ফাংশন কাজ করে? pos *= 2সম্পূর্ণ স্বেচ্ছাচারী বলে মনে হচ্ছে। এর তাত্পর্য কী?
2mac

1
@ 2 ম্যাক এক্সফোনেনশিয়াল অনুসন্ধান । এটি পর্যাপ্ত রেখাগুলি না পাওয়া পর্যন্ত প্রতিটি বারের পরিমাণ দ্বিগুণ করে পুনরুক্তিযুক্ত ফাইলের শেষে থেকে এটি পড়ে।
এ Coady

আমি মনে করি যে শেষ থেকে পড়ার সমাধানটি ইউটিএফ -8 এর সাথে এনকোডযুক্ত ফাইলগুলিকে সমর্থন করবে না, যেহেতু অক্ষরের দৈর্ঘ্য পরিবর্তনশীল, এবং আপনি (সম্ভবতঃ) এমন কোনও অদ্ভুত অফসেটে ল্যান্ড করতে পারেন যা সঠিকভাবে ব্যাখ্যা করা যায় না।
মাইক

দুর্ভাগ্যক্রমে আপনার দুরন্ত অনুসন্ধান সমাধান অজগর 3 এর জন্য কাজ করে না। যেমন f.seek () নেতিবাচক অফসেট নেয় না। অজগর 3 লিঙ্কটির
ইটজওয়ালা

25

উপরের এস.লোটের উত্তর আমার জন্য প্রায় কাজ করে তবে শেষ হয় আমাকে আংশিক লাইন দেওয়া। দেখা যাচ্ছে যে এটি ব্লক সীমানায় ডেটা দূষিত করে কারণ ডেটা বিপরীত ক্রমে রিড ব্লক ধারণ করে। যখন '' .জয়াইন (ডেটা) বলা হয়, তখন ব্লকগুলি ভুল ক্রমে থাকে। এটি ঠিক করে দেয়।

def tail(f, window=20):
    """
    Returns the last `window` lines of file `f` as a list.
    f - a byte file-like object
    """
    if window == 0:
        return []
    BUFSIZ = 1024
    f.seek(0, 2)
    bytes = f.tell()
    size = window + 1
    block = -1
    data = []
    while size > 0 and bytes > 0:
        if bytes - BUFSIZ > 0:
            # Seek back one whole BUFSIZ
            f.seek(block * BUFSIZ, 2)
            # read BUFFER
            data.insert(0, f.read(BUFSIZ))
        else:
            # file too small, start from begining
            f.seek(0,0)
            # only read what was not read
            data.insert(0, f.read(bytes))
        linesFound = data[0].count('\n')
        size -= linesFound
        bytes -= BUFSIZ
        block -= 1
    return ''.join(data).splitlines()[-window:]

1
তালিকার শুরুতে সন্নিবেশ করা একটি খারাপ ধারণা। কেন যোগ্য কাঠামো ব্যবহার করবেন না?
সের্গেই 11 জি

1
দু: খজনকভাবে পাইথন 3 সামঞ্জস্যপূর্ণ নয় ... এটি কেন চেষ্টা করার চেষ্টা করছে।
শার্লক 70

20

কোডটি আমি ব্যবহার করে শেষ করেছি। আমি মনে করি এটি এখন পর্যন্ত সেরা:

def tail(f, n, offset=None):
    """Reads a n lines from f with an offset of offset lines.  The return
    value is a tuple in the form ``(lines, has_more)`` where `has_more` is
    an indicator that is `True` if there are more lines in the file.
    """
    avg_line_length = 74
    to_read = n + (offset or 0)

    while 1:
        try:
            f.seek(-(avg_line_length * to_read), 2)
        except IOError:
            # woops.  apparently file is smaller than what we want
            # to step back, go to the beginning instead
            f.seek(0)
        pos = f.tell()
        lines = f.read().splitlines()
        if len(lines) >= to_read or pos == 0:
            return lines[-to_read:offset and -offset or None], \
                   len(lines) > to_read or pos > 0
        avg_line_length *= 1.3

5
প্রশ্নের ঠিক উত্তর দেয় না।
শেখ

13

এমএমএপ সহ সহজ এবং দ্রুত সমাধান:

import mmap
import os

def tail(filename, n):
    """Returns last n lines from the filename. No exception handling"""
    size = os.path.getsize(filename)
    with open(filename, "rb") as f:
        # for Windows the mmap parameters are different
        fm = mmap.mmap(f.fileno(), 0, mmap.MAP_SHARED, mmap.PROT_READ)
        try:
            for i in xrange(size - 1, -1, -1):
                if fm[i] == '\n':
                    n -= 1
                    if n == -1:
                        break
            return fm[i + 1 if i else 0:].splitlines()
        finally:
            fm.close()

1
এটি সম্ভবত দ্রুততম উত্তর যখন ইনপুটটি বিশাল হতে পারে (বা এটি হতে পারে, যদি .rfindপাইথন স্তরে একবারে চেক পরীক্ষা করে বাইট না করে নতুন লাইনের জন্য পিছনের স্ক্যান করার পদ্ধতিটি ব্যবহার করা হয় ; সিপাইথনে, পাইথন স্তরের কোডটি প্রতিস্থাপন করে সি অন্তর্নির্মিত কলগুলি সাধারণত প্রচুর পরিমাণে জয়ী হয়)। ছোট ইনপুটগুলির জন্য, একটি dequeসহ maxlenসহজ এবং সম্ভবত একইভাবে দ্রুত।
শ্যাডোর্যাঞ্জার

4

এমনকি ক্লিনার পাইথন 3 সামঞ্জস্যপূর্ণ সংস্করণ যা সন্নিবেশ করে না তবে সংযোজন ও বিপরীতগুলি:

def tail(f, window=1):
    """
    Returns the last `window` lines of file `f` as a list of bytes.
    """
    if window == 0:
        return b''
    BUFSIZE = 1024
    f.seek(0, 2)
    end = f.tell()
    nlines = window + 1
    data = []
    while nlines > 0 and end > 0:
        i = max(0, end - BUFSIZE)
        nread = min(end, BUFSIZE)

        f.seek(i)
        chunk = f.read(nread)
        data.append(chunk)
        nlines -= chunk.count(b'\n')
        end -= nread
    return b'\n'.join(b''.join(reversed(data)).splitlines()[-window:])

এটি এর মতো ব্যবহার করুন:

with open(path, 'rb') as f:
    last_lines = tail(f, 3).decode('utf-8')

খুব নড়বড়ে নয় - তবে আমি সাধারণভাবে প্রচুর উত্তর সহ 10 বছরের পুরানো প্রশ্নের উত্তর যুক্ত না করার পরামর্শ দেব। তবে আমাকে সাহায্য করুন: আপনার কোডে পাইথন 3 এর সাথে কী নির্দিষ্ট?
usr2564301

অন্যান্য উত্তর ঠিক কাজ হয় নি ভাল :-) py3: দেখুন stackoverflow.com/questions/136168/...
Hauke Rehfeld

3

পাইথন 3 এ পেপারক্রেনের সমাধানটি আপডেট করুন। open(filename, 'rb')এবং এর সাথে ফাইলটি খুলুন :

def tail(f, window=20):
    """Returns the last `window` lines of file `f` as a list.
    """
    if window == 0:
        return []

    BUFSIZ = 1024
    f.seek(0, 2)
    remaining_bytes = f.tell()
    size = window + 1
    block = -1
    data = []

    while size > 0 and remaining_bytes > 0:
        if remaining_bytes - BUFSIZ > 0:
            # Seek back one whole BUFSIZ
            f.seek(block * BUFSIZ, 2)
            # read BUFFER
            bunch = f.read(BUFSIZ)
        else:
            # file too small, start from beginning
            f.seek(0, 0)
            # only read what was not read
            bunch = f.read(remaining_bytes)

        bunch = bunch.decode('utf-8')
        data.insert(0, bunch)
        size -= bunch.count('\n')
        remaining_bytes -= BUFSIZ
        block -= 1

    return ''.join(data).splitlines()[-window:]

3

আমার অনুরূপ প্রশ্নের উত্তরে মন্তব্যকারীদের অনুরোধে একটি উত্তর পোস্ট করা যেখানে একই কৌশলটি কোনও ফাইলের শেষ লাইনটি পরিবর্তনের জন্য ব্যবহার করা হয়েছিল, কেবল এটিই পাওয়া গেল না।

গুরুত্বপূর্ণ আকারের একটি ফাইলের জন্য, mmapএটি করার সর্বোত্তম উপায়। বিদ্যমান mmapউত্তরের উন্নতি করতে , এই সংস্করণটি উইন্ডোজ এবং লিনাক্সের মধ্যে বহনযোগ্য এবং দ্রুত চালিত হওয়া উচিত (যদিও এটি জিবি রেঞ্জের ফাইলগুলির সাথে 32 বিট পাইথন-এ কিছু পরিবর্তন না করে কাজ করবে না, এটি পরিচালনা করার জন্য অন্যান্য উত্তর দেখুন , এবং পাইথন 2 এ কাজ করতে সংশোধন করার জন্য )।

import io  # Gets consistent version of open for both Py2.7 and Py3.x
import itertools
import mmap

def skip_back_lines(mm, numlines, startidx):
    '''Factored out to simplify handling of n and offset'''
    for _ in itertools.repeat(None, numlines):
        startidx = mm.rfind(b'\n', 0, startidx)
        if startidx < 0:
            break
    return startidx

def tail(f, n, offset=0):
    # Reopen file in binary mode
    with io.open(f.name, 'rb') as binf, mmap.mmap(binf.fileno(), 0, access=mmap.ACCESS_READ) as mm:
        # len(mm) - 1 handles files ending w/newline by getting the prior line
        startofline = skip_back_lines(mm, offset, len(mm) - 1)
        if startofline < 0:
            return []  # Offset lines consumed whole file, nothing to return
            # If using a generator function (yield-ing, see below),
            # this should be a plain return, no empty list

        endoflines = startofline + 1  # Slice end to omit offset lines

        # Find start of lines to capture (add 1 to move from newline to beginning of following line)
        startofline = skip_back_lines(mm, n, startofline) + 1

        # Passing True to splitlines makes it return the list of lines without
        # removing the trailing newline (if any), so list mimics f.readlines()
        return mm[startofline:endoflines].splitlines(True)
        # If Windows style \r\n newlines need to be normalized to \n, and input
        # is ASCII compatible, can normalize newlines with:
        # return mm[startofline:endoflines].replace(os.linesep.encode('ascii'), b'\n').splitlines(True)

এটি ধরে নেওয়া হয়েছে যে লেজযুক্ত রেখাগুলির সংখ্যা যথেষ্ট কম আপনি নিরাপদে সেগুলি একবারে স্মৃতিতে পড়তে পারেন; আপনি এটিকে একটি জেনারেটর ফাংশনও করতে পারেন এবং চূড়ান্ত লাইনটি প্রতিস্থাপনের মাধ্যমে একবারে ম্যানুয়ালি একটি লাইন পড়তে পারেন:

        mm.seek(startofline)
        # Call mm.readline n times, or until EOF, whichever comes first
        # Python 3.2 and earlier:
        for line in itertools.islice(iter(mm.readline, b''), n):
            yield line

        # 3.3+:
        yield from itertools.islice(iter(mm.readline, b''), n)

শেষ অবধি mmap, এটি বাইনারি মোডে (পড়ার জন্য প্রয়োজনীয় ) পড়ে তাই এটি strলাইন দেয় (পাই 2) এবং bytesলাইনগুলি (পাই 3); আপনি যদি unicode(পাই 2) বা str(পাই 3) চান তবে পুনরাবৃত্ত পদ্ধতির আপনার জন্য ডিকোড করতে এবং / অথবা নিউলাইনগুলি ঠিক করতে পারে:

        lines = itertools.islice(iter(mm.readline, b''), n)
        if f.encoding:  # Decode if the passed file was opened with a specific encoding
            lines = (line.decode(f.encoding) for line in lines)
        if 'b' not in f.mode:  # Fix line breaks if passed file opened in text mode
            lines = (line.replace(os.linesep, '\n') for line in lines)
        # Python 3.2 and earlier:
        for line in lines:
            yield line
        # 3.3+:
        yield from lines

দ্রষ্টব্য: আমি এগুলি সমস্ত এমন কোনও মেশিনে টাইপ করেছি যেখানে পরীক্ষার জন্য পাইথনে আমার অ্যাক্সেসের অভাব রয়েছে। আমি কিছু টাইপ করলে দয়া করে আমাকে জানান; এটি আমার অন্যান্য উত্তরের মতোই যথেষ্ট ছিল যা আমি মনে করি এটি কাজ করা উচিত, তবে টুইটগুলি (উদাহরণস্বরূপ একটি পরিচালনা করা offset) সূক্ষ্ম ত্রুটির দিকে পরিচালিত করতে পারে। কোনও ভুল থাকলে দয়া করে আমাকে মন্তব্যে জানান know


3

আমি পপেনকে সর্বোত্তম সমাধান হিসাবে পেয়েছি। এটি দ্রুত এবং নোংরা এবং এটি ইউনিক্স মেশিনে পাইথন ২.6 এর জন্য কাজ করে আমি নিম্নলিখিতটি ব্যবহার করেছি

def GetLastNLines(self, n, fileName):
    """
    Name:           Get LastNLines
    Description:        Gets last n lines using Unix tail
    Output:         returns last n lines of a file
    Keyword argument:
    n -- number of last lines to return
    filename -- Name of the file you need to tail into
    """
    p = subprocess.Popen(['tail','-n',str(n),self.__fileName], stdout=subprocess.PIPE)
    soutput, sinput = p.communicate()
    return soutput

soutput কোডের শেষ এন লাইন থাকবে। লাইন দ্বারা সাউপুট লাইন মাধ্যমে পুনরাবৃত্তি করতে:

for line in GetLastNLines(50,'myfile.log').split('\n'):
    print line

2

এস.লোটের শীর্ষ ভোটের উত্তরের ভিত্তিতে (সেপ্টেম্বর 25 '08 এ 21:43 এ), তবে ছোট ফাইলগুলির জন্য স্থির।

def tail(the_file, lines_2find=20):  
    the_file.seek(0, 2)                         #go to end of file
    bytes_in_file = the_file.tell()             
    lines_found, total_bytes_scanned = 0, 0
    while lines_2find+1 > lines_found and bytes_in_file > total_bytes_scanned: 
        byte_block = min(1024, bytes_in_file-total_bytes_scanned)
        the_file.seek(-(byte_block+total_bytes_scanned), 2)
        total_bytes_scanned += byte_block
        lines_found += the_file.read(1024).count('\n')
    the_file.seek(-total_bytes_scanned, 2)
    line_list = list(the_file.readlines())
    return line_list[-lines_2find:]

    #we read at least 21 line breaks from the bottom, block by block for speed
    #21 to ensure we don't get a half line

আশা করি এটি কার্যকর হবে।


2

পাইপিতে লেজের কিছু বিদ্যমান বাস্তবায়ন রয়েছে যা আপনি পাইপ ব্যবহার করে ইনস্টল করতে পারেন:

  • mtFileUtil
  • multitail
  • log4tailer
  • ...

আপনার পরিস্থিতির উপর নির্ভর করে এই বিদ্যমান সরঞ্জামগুলির মধ্যে একটি ব্যবহার করার সুবিধা থাকতে পারে।


আপনি কি উইন্ডোজে কাজ করে এমন কোনও মডিউল সম্পর্কে সচেতন? আমি চেষ্টা করেছি tailhead, tailerকিন্তু তারা কাজ করেনি। চেষ্টাও করেছি mtFileUtil। এটি প্রাথমিকভাবে ত্রুটি ছুঁড়েছিল কারণ printবিবৃতিগুলির প্রথম বন্ধনী ছিল না (আমি পাইথন ৩.6 এ আছি)। আমি সেগুলিতে যুক্ত করেছি reverse.pyএবং ত্রুটি বার্তাগুলি চলে গেছে তবে যখন আমার স্ক্রিপ্টটি মডিউলটিকে কল করে ( mtFileUtil.tail(open(logfile_path), 5)), এটি কিছুই মুদ্রণ করে না।
টেকনেক্সট

2

সাধারণ:

with open("test.txt") as f:
data = f.readlines()
tail = data[-2:]
print(''.join(tail)

এটি সম্পূর্ণ বাস্তবায়ন খারাপ implementation বিশাল ফাইল হ্যান্ডলিংয়ের কথা বিবেচনা করুন এবং যেখানে
এনও

1

খুব বড় ফাইলগুলির সাথে দক্ষতার জন্য (লগফাইলে পরিস্থিতিতে যেখানে আপনি লেজ ব্যবহার করতে পারেন) আপনি সাধারণত পুরো ফাইলটি পড়া এড়াতে চান (এমনকি যদি আপনি একবারে পুরো ফাইলটি মেমরির মধ্যে না পড়ে এটি করেন) তবে আপনি এটি করেন অক্ষরের চেয়ে লাইনগুলিতে অফসেটটি কোনওভাবে কাজ করা দরকার। একটি সম্ভাবনা চর দ্বারা সন্ধান () চর নিয়ে পিছন দিকে পড়া, তবে এটি খুব ধীর slow পরিবর্তে বৃহত্তর ব্লকগুলিতে প্রক্রিয়া করা ভাল।

আমার এখানে একটি ইউটিলিটি ফাংশন রয়েছে যা আমি এখানে আগে ব্যবহৃত ফাইলগুলি পিছনের দিকে পড়তে লিখেছিলাম।

import os, itertools

def rblocks(f, blocksize=4096):
    """Read file as series of blocks from end of file to start.

    The data itself is in normal order, only the order of the blocks is reversed.
    ie. "hello world" -> ["ld","wor", "lo ", "hel"]
    Note that the file must be opened in binary mode.
    """
    if 'b' not in f.mode.lower():
        raise Exception("File must be opened using binary mode.")
    size = os.stat(f.name).st_size
    fullblocks, lastblock = divmod(size, blocksize)

    # The first(end of file) block will be short, since this leaves 
    # the rest aligned on a blocksize boundary.  This may be more 
    # efficient than having the last (first in file) block be short
    f.seek(-lastblock,2)
    yield f.read(lastblock)

    for i in range(fullblocks-1,-1, -1):
        f.seek(i * blocksize)
        yield f.read(blocksize)

def tail(f, nlines):
    buf = ''
    result = []
    for block in rblocks(f):
        buf = block + buf
        lines = buf.splitlines()

        # Return all lines except the first (since may be partial)
        if lines:
            result.extend(lines[1:]) # First line may not be complete
            if(len(result) >= nlines):
                return result[-nlines:]

            buf = lines[0]

    return ([buf]+result)[-nlines:]


f=open('file_to_tail.txt','rb')
for line in tail(f, 20):
    print line

[সম্পাদনা] আরও সুনির্দিষ্ট সংস্করণ যুক্ত করা হয়েছে (দুবার বিপরীত হওয়া এড়ানো হবে)


দ্রুত পরীক্ষাগুলি দেখায় যে এটি উপরের থেকে আমার সংস্করণটির চেয়ে অনেক খারাপ কাজ করে। সম্ভবত আপনার বাফারিংয়ের কারণে।
আর্মিন রোনাচার

আমার সন্দেহ হয় কারণ আমি একাধিক সন্ধান পিছনের দিকে করছি, তাই রিডহেড বাফারটি তেমন ভাল ব্যবহার পাচ্ছে না। তবে, আমি মনে করি যখন লাইন দৈর্ঘ্যে আপনার অনুমান সঠিক না হয় (উদাহরণস্বরূপ খুব বড় লাইন), কারণ এটি এই ক্ষেত্রে ডেটাটি পুনরায় পড়তে হবে এড়ানো এটি আরও ভাল করতে পারে।
ব্রায়ান

1

আপনি f.seek (0, 2) দিয়ে আপনার ফাইলের শেষে যেতে পারেন এবং তারপরে রিডলাইন () এর জন্য নিম্নলিখিত প্রতিস্থাপনের সাথে একের পর এক লাইনগুলি পড়তে পারেন:

def readline_backwards(self, f):
    backline = ''
    last = ''
    while not last == '\n':
        backline = last + backline
        if f.tell() <= 0:
            return backline
        f.seek(-1, 1)
        last = f.read(1)
        f.seek(-1, 1)
    backline = last
    last = ''
    while not last == '\n':
        backline = last + backline
        if f.tell() <= 0:
            return backline
        f.seek(-1, 1)
        last = f.read(1)
        f.seek(-1, 1)
    f.seek(1, 1)
    return backline

1

আইকিউ উত্তরের উপর ভিত্তি করে (জুন 10 '10 এ 21: 28): এই শ্রেণিটি অবজেক্ট ফাইল করতে মাথা () এবং লেজ () পদ্ধতি যুক্ত করে।

class File(file):
    def head(self, lines_2find=1):
        self.seek(0)                            #Rewind file
        return [self.next() for x in xrange(lines_2find)]

    def tail(self, lines_2find=1):  
        self.seek(0, 2)                         #go to end of file
        bytes_in_file = self.tell()             
        lines_found, total_bytes_scanned = 0, 0
        while (lines_2find+1 > lines_found and
               bytes_in_file > total_bytes_scanned): 
            byte_block = min(1024, bytes_in_file-total_bytes_scanned)
            self.seek(-(byte_block+total_bytes_scanned), 2)
            total_bytes_scanned += byte_block
            lines_found += self.read(1024).count('\n')
        self.seek(-total_bytes_scanned, 2)
        line_list = list(self.readlines())
        return line_list[-lines_2find:]

ব্যবহার:

f = File('path/to/file', 'r')
f.head(3)
f.tail(3)

1

ফাইলটি end n এর মধ্যে শেষ না হওয়া বা সম্পূর্ণ প্রথম লাইনটি পড়ার বিষয়টি নিশ্চিত করার ক্ষেত্রে এই সমাধানগুলির বেশ কয়েকটিতে সমস্যা রয়েছে।

def tail(file, n=1, bs=1024):
    f = open(file)
    f.seek(-1,2)
    l = 1-f.read(1).count('\n') # If file doesn't end in \n, count it anyway.
    B = f.tell()
    while n >= l and B > 0:
            block = min(bs, B)
            B -= block
            f.seek(B, 0)
            l += f.read(block).count('\n')
    f.seek(B, 0)
    l = min(l,n) # discard first (incomplete) line if l > n
    lines = f.readlines()[-l:]
    f.close()
    return lines

1

এখানে একটি সুন্দর সরল বাস্তবায়ন:

with open('/etc/passwd', 'r') as f:
  try:
    f.seek(0,2)
    s = ''
    while s.count('\n') < 11:
      cur = f.tell()
      f.seek((cur - 10))
      s = f.read(10) + s
      f.seek((cur - 10))
    print s
  except Exception as e:
    f.readlines()

দুর্দান্ত উদাহরণ! আপনি দয়া করে এর আগে চেষ্টা ব্যবহার ব্যাখ্যা করতে পারেন f.seek? কেন আগে না with open? এছাড়াও, কেন exceptআপনি একটি করতে f.readlines()??

সত্যিই, চেষ্টাটি প্রথমে করা উচিত .. স্বাস্থ্যকর স্ট্যান্ডার্ড লিনাক্স সিস্টেম ব্যতীত খোলা () খোলা না ধরার কোনও কারণ আমার মনে নেই, / ইত্যাদি / পাসডব্লিউড সবসময় পঠনযোগ্য হওয়া উচিত। চেষ্টা করুন, তারপরে আরও সাধারণ ক্রম।
GL2014


1

আরেকটি সমাধান

যদি আপনার টেক্সট ফাইলটি এর মতো দেখায়: মাউস সাপ বিড়াল টিকটিকির নেকড়ে কুকুর

আপনি কেবল অজগর '' 'এ অ্যারে সূচক ব্যবহার করে এই ফাইলটি বিপরীত করতে পারেন

contents=[]
def tail(contents,n):
    with open('file.txt') as file:
        for i in file.readlines():
            contents.append(i)

    for i in contents[:n:-1]:
        print(i)

tail(contents,-5)

ফলাফল: কুকুর নেকড়ে টিকটিকি বিড়াল


1

সবচেয়ে সহজ উপায়টি হ'ল deque:

from collections import deque

def tail(filename, n=10):
    with open(filename) as f:
        return deque(f, n)

0

আমাকে একটি ফাইলের শেষ লাইন থেকে একটি নির্দিষ্ট মান পড়তে হয়েছিল এবং এই থ্রেডে হোঁচট খেতে হয়েছিল। পাইথনের চাকাটিকে পুনরায় উদ্ভাবন করার পরিবর্তে আমি একটি ছোট শেল স্ক্রিপ্ট দিয়ে শেষ করেছি, / usr / স্থানীয় / বিন / get_last_netp হিসাবে সংরক্ষণ করা হয়েছে:

#! /bin/bash
tail -n1 /home/leif/projects/transfer/export.log | awk {'print $14'}

পাইথন প্রোগ্রামে:

from subprocess import check_output

last_netp = int(check_output("/usr/local/bin/get_last_netp"))

0

ডেক ব্যবহার করে প্রথম উদাহরণ নয়, বরং আরও সহজ simp এটি সাধারণ: এটি কোনও ফাইল নয়, কোনও পুনরাবৃত্তিযোগ্য অবজেক্টে কাজ করে।

#!/usr/bin/env python
import sys
import collections
def tail(iterable, N):
    deq = collections.deque()
    for thing in iterable:
        if len(deq) >= N:
            deq.popleft()
        deq.append(thing)
    for thing in deq:
        yield thing
if __name__ == '__main__':
    for line in tail(sys.stdin,10):
        sys.stdout.write(line)

0
This is my version of tailf

import sys, time, os

filename = 'path to file'

try:
    with open(filename) as f:
        size = os.path.getsize(filename)
        if size < 1024:
            s = size
        else:
            s = 999
        f.seek(-s, 2)
        l = f.read()
        print l
        while True:
            line = f.readline()
            if not line:
                time.sleep(1)
                continue
            print line
except IOError:
    pass

0
import time

attemps = 600
wait_sec = 5
fname = "YOUR_PATH"

with open(fname, "r") as f:
    where = f.tell()
    for i in range(attemps):
        line = f.readline()
        if not line:
            time.sleep(wait_sec)
            f.seek(where)
        else:
            print line, # already has newline

0
import itertools
fname = 'log.txt'
offset = 5
n = 10
with open(fname) as f:
    n_last_lines = list(reversed([x for x in itertools.islice(f, None)][-(offset+1):-(offset+n+1):-1]))

0
abc = "2018-06-16 04:45:18.68"
filename = "abc.txt"
with open(filename) as myFile:
    for num, line in enumerate(myFile, 1):
        if abc in line:
            lastline = num
print "last occurance of work at file is in "+str(lastline) 

0

এ.কোয়াদির দেওয়া উত্তরের জন্য আপডেট

সঙ্গে কাজ করে পাইথন 3

এটি এক্সফোনেনশিয়াল অনুসন্ধান ব্যবহার করে এবং কেবল Nপিছন থেকে লাইনগুলি বাফার করবে এবং খুব দক্ষ।

import time
import os
import sys

def tail(f, n):
    assert n >= 0
    pos, lines = n+1, []

    # set file pointer to end

    f.seek(0, os.SEEK_END)

    isFileSmall = False

    while len(lines) <= n:
        try:
            f.seek(f.tell() - pos, os.SEEK_SET)
        except ValueError as e:
            # lines greater than file seeking size
            # seek to start
            f.seek(0,os.SEEK_SET)
            isFileSmall = True
        except IOError:
            print("Some problem reading/seeking the file")
            sys.exit(-1)
        finally:
            lines = f.readlines()
            if isFileSmall:
                break

        pos *= 2

    print(lines)

    return lines[-n:]




with open("stream_logs.txt") as f:
    while(True):
        time.sleep(0.5)
        print(tail(f,2))

-1

দ্বিতীয় চিন্তায়, এটি সম্ভবত এখানে যে কোনও কিছুর তত দ্রুত।

def tail( f, window=20 ):
    lines= ['']*window
    count= 0
    for l in f:
        lines[count%window]= l
        count += 1
    print lines[count%window:], lines[:count%window]

এটা অনেক সহজ। এবং এটি একটি ভাল গতি বরাবর ছিঁড়ে গেছে বলে মনে হচ্ছে না।


কারণ এখানে প্রায় সমস্ত কিছু 30 মেগাবাইটের বেশি বা লগ ফাইলগুলিতে প্রায় একই পরিমাণে মেমরি লোড না করে র‌্যামে কাজ করে না;) আপনার প্রথম সংস্করণটি অনেক ভাল, তবে এখানে পরীক্ষার ফাইলগুলির জন্য এটি আমার চেয়ে কিছুটা খারাপ সম্পাদন করে এবং এটি বিভিন্ন নিউলাইন চরিত্রের সাথে কাজ করে না।
আর্মিন রোনাচার

3
আমি ভৃল ছিলাম. সংস্করণ 1 অভিধানের মাধ্যমে 10 লেজের জন্য 0.00248908996582 নিয়েছিল। সংস্করণ 2 অভিধানের মাধ্যমে 10 লেজের জন্য 1.2963051796 নিয়েছিল। আমি প্রায় নিজেকে ভোট দিতে হবে।
এস .লট

"বিভিন্ন নিউলাইন চরিত্রের সাথে কাজ করে না" " যদি গুরুত্বপূর্ণ হয়ে থাকে তবে ডেটাউন্ট ('\ n') লেন (ডেটা.স্প্লিটলাইন ()) এর সাথে প্রতিস্থাপন করুন।
এস .লট
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.