Как читать и извлекать информацию из файла, который постоянно обновляется? - PullRequest
7 голосов
/ 10 сентября 2010

Вот как я планирую построить свои утилиты для проекта:

  • logdump выводит результаты журнала в файл log .Результаты добавляются к существующим результатам, если файл уже существует (например, если новый файл создается каждый месяц, результаты добавляются в один и тот же файл за этот месяц).

  • extract читает файл результатов журнала, чтобы извлечь соответствующие результаты в зависимости от предоставленных аргументов.

  • Дело в том, что я не хочу ждать logdump для завершения записи в log для начала обработки.Кроме того, мне нужно будет помнить до того момента, когда я уже прочитал log , чтобы начать извлекать дополнительную информацию, а это не то, чем я хочу заниматься.

  • Мне нужно житьрезультаты, так что всякий раз, когда что-то добавляется в файл результатов журнала, extract будет получать требуемые результаты.

  • Обработка, которую extract сделает, будет общей (будет зависеть отнекоторые аргументы командной строки), но, конечно, построчно.

Это включает в себя чтение файла во время его записи и постоянный мониторинг его новых обновленийдаже после того, как вы достигнете конца файла log .

Как я могу сделать это с помощью C или C ++ или сценариев оболочки или Perl?

Ответы [ 3 ]

15 голосов
/ 10 сентября 2010

tail -f будет считывать из файла и отслеживать его на наличие обновлений, когда он достигнет EOF, вместо того, чтобы сразу выйти.Это простой способ прочитать файл журнала "вживую".Может быть так просто:

tail -f log.file | extract

Или, может быть, tail -n 0 -f, поэтому он печатает только новые строки, а не существующиеИли tail -n +0 -f, чтобы отобразить весь файл, а затем продолжить обновление.

9 голосов
/ 10 сентября 2010

Традиционным Unix-инструментом для этого является tail -f, который сохраняет данные, добавленные к его аргументу, до тех пор, пока вы их не уничтожите.Таким образом, вы можете сделать

tail -c +1 -f log | extract

В мире Unix чтение из постоянно добавляемых файлов стало известно как «хвост».В Perl модуль File :: Tail выполняет ту же задачу.

use File::Tail;
my $log_file = File::Tail->new("log");
while (defined (my $log_line = $log_file->read)) {
    process_line($log_line);
}
6 голосов
/ 10 сентября 2010

Использование простой замены для logdump

#! /usr/bin/perl

use warnings;
use strict;

open my $fh, ">", "log" or die "$0: open: $!";
select $fh;
$| = 1;  # disable buffering

for (1 .. 10) {
  print $fh "message $_\n" or warn "$0: print: $!";
  sleep rand 5;
}

и скелет для extract ниже, чтобы получить необходимую обработку. Когда logfile встречает конец файла, logfile.eof() имеет значение true. Вызов logfile.clear() сбрасывает все состояние ошибки, а затем мы спим и пытаемся снова.

#include <iostream>
#include <fstream>
#include <cerrno>
#include <cstring>
#include <unistd.h>

int main(int argc, char *argv[])
{
  const char *path;
  if      (argc == 2) path = argv[1];
  else if (argc == 1) path = "log";
  else {
    std::cerr << "Usage: " << argv[0] << " [ log-file ]\n";
    return 1;
  }

  std::ifstream logfile(path);
  std::string line;
  next_line: while (std::getline(logfile, line))
    std::cout << argv[0] << ": extracted [" << line << "]\n";

  if (logfile.eof()) {
    sleep(3);
    logfile.clear();
    goto next_line;
  }
  else {
    std::cerr << argv[0] << ": " << path << ": " << std::strerror(errno) << '\n';
    return 1;
  }

  return 0;
}

Это не так интересно, как смотреть его вживую, но на выходе получается

./extract: extracted [message 1]
./extract: extracted [message 2]
./extract: extracted [message 3]
./extract: extracted [message 4]
./extract: extracted [message 5]
./extract: extracted [message 6]
./extract: extracted [message 7]
./extract: extracted [message 8]
./extract: extracted [message 9]
./extract: extracted [message 10]
^C

Я оставил прерывание на выходе, чтобы подчеркнуть, что это бесконечный цикл.

Используйте Perl в качестве языка склеивания, чтобы extract получал строки из журнала с помощью tail:

#! /usr/bin/perl

use warnings;
use strict;

die "Usage: $0 [ log-file ]\n" if @ARGV > 1;
my $path = @ARGV ? shift : "log";

open my $fh, "-|", "tail", "-c", "+1", "-f", $path
  or die "$0: could not start tail: $!";

while (<$fh>) {
  chomp;
  print "$0: extracted [$_]\n";
}

Наконец, если вы настаиваете на том, чтобы выполнять тяжелую работу самостоятельно, есть связанных с Perl FAQ :

Как мне сделать tail -f в Perl?

Первая попытка

seek(GWFILE, 0, 1);

Оператор seek(GWFILE, 0, 1) не меняет текущую позицию, но он очищает условие конца файла на дескрипторе, поэтому следующий <GWFILE> заставляет Perl снова пытаться что-то прочитать.

Если это не сработает (зависит от особенностей вашей реализации stdio), тогда вам нужно что-то похожее на это:

for (;;) {
  for ($curpos = tell(GWFILE); <GWFILE>; $curpos = tell(GWFILE)) {
    # search for some stuff and put it into files
  }
  # sleep for a while
  seek(GWFILE, $curpos, 0);  # seek to where we had been
}

Если это все еще не работает, посмотрите на метод clearerr из IO::Handle, который сбрасывает ошибки и состояния конца файла в дескрипторе.

Также есть модуль File::Tail от CPAN.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...