LoginSignup
15
14

More than 5 years have passed since last update.

tailを実装する

Last updated at Posted at 2015-06-23

Rubyでファイルの末尾n行を表示するtailっぽい何かを作った

をまねてみる。

シンプル版

tail_simple.rb
file_path = ARGV[0]
row_limit = ARGV[1].to_i

buffer = []
File.foreach(file_path) do |line|
  buffer << line
  buffer.shift if buffer.size > row_limit
end

puts buffer

まず、元の記事のコメント欄より。Rubyでやるならこれでいい気がする。この特徴は

  • シンプル
  • ファイルを全部読む
  • メモリ使用量は表示する分

など。試してみると意外と早い。

バッファー版

tail_buffered.rb

def reverse_chunks(file, size)
  n = file.size / size
  n -= 1 if file.size == n * size
  len = file.size - n * size
  until n < 0
    file.seek(n * size)
    yield file.read(len)
    n -= 1
    len = size
  end
end

def offset_of_nth_chr_from_tail(file, count, target)
  offset = 0
  reverse_chunks(file, 1024*16) do |chunk|
    chunk.size.times do |i|
      chr = chunk[chunk.size - i - 1]
      if chr == target || (offset == 0 && i == 0 && chr != target)
        count -= 1

        if count < 0
          offset += i
          return offset
        end
      end
    end
    offset += chunk.size
  end

  offset
end

def tail(fname, n_lines)
  open(fname) do |file|
    offset = offset_of_nth_chr_from_tail(file, n_lines, "\n")
    file.seek(file.size - offset)
    print file.read
  end
end

tail(ARGV[0], ARGV[1].to_i)



次に自分で書いたRubyによるtail。ファイルをある程度のサイズの塊に分割して、後ろの塊のその後ろから改行の数を数える方式。

  • 実装が複雑
  • メモリー使用量は塊のサイズ
  • 後ろから読むのででかいファイルも大丈夫

あんまり早くない。

mmap版

次はtailを書けって言われたらこの方向にするだろうという実装。

tail_mmap.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>

void tail(char *fname, int n_lines) {
  int fd = open(fname, O_RDONLY);
  struct stat st;
  fstat(fd, &st);
  int fsize = st.st_size;

  char *map = mmap(0, fsize, PROT_READ, MAP_PRIVATE, fd, 0);
  char *cursor = map+fsize;

  if (fsize > 0 && cursor[-1] != '\n') n_lines--;

  for (int i = 0; i < fsize; i++){
    cursor--;

    if (*cursor == '\n') n_lines--;

    if (n_lines < 0){
      cursor++;
      break;
    }
  }

  write(1, cursor, fsize-(cursor-map));

  munmap(map, fsize);
  close(fd);
}

int main(int argc, char *argv[]){
  if(argc != 3)  exit(EXIT_FAILURE);

  tail(argv[1], atoi(argv[2]));

  exit(EXIT_SUCCESS);
}

mmap使って普通に後ろから数える方式。

  • 割とシンプル
  • mmapが環境依存
  • mmapのメモリ使用量はどうなるんだろう?多くはないと思う
  • 後ろから読むのででかいファイルも大丈夫

とメリットが多い。あとすごい早い。オリジナルのtailより早い。

15
14
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
14