如何从不断更新的文件中读取和提取信息?

时间:2010-09-09 20:14:01

标签: c++ c perl shell

这就是我计划为项目构建实用程序的方法:

  • logdump 将日志结果转储到文件日志。如果文件已存在,结果将附加到现有结果中(如果每月创建一个新文件,结果将附加到该月的同一文件中)。

  • extract 读取日志结果文件,以根据提供的参数提取相关结果。

  • 问题是我不想等待 logdump 完成写入日志以开始处理它。同样,我需要记住,直到我已经阅读 log 开始提取更多信息,这不是我想要做的。

  • 我需要实时结果,以便每当有什么内容添加到日志结果文件中时,提取将获得所需的结果。

  • 提取所做的处理将是通用的(取决于它的一些命令行参数),但肯定是逐行的。

这涉及在文件被写入时读取文件,并在文件到达日志文件的末尾时连续监视文件以获取新的更新。

如何使用C或C ++或shell脚本或Perl执行此操作?

3 个答案:

答案 0 :(得分:15)

tail -f将从文件中读取并在达到EOF时监视它以获取更新,而不是直接退出。这是一种简单的方法来读取“实时”日志文件。可以这么简单:

tail -f log.file | extract

或者也许tail -n 0 -f所以它只打印新行,而不是现有行。或tail -n +0 -f显示整个文件,然后继续更新。

答案 1 :(得分:9)

传统的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);
}

答案 2 :(得分:6)

使用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()为真。调用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";
}

最后,如果你坚持自己做重物,那就是related Perl FAQ

  

如何在perl中执行tail -f?

     

首先尝试

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
}
     

如果仍然无效,请查看IO::Handle中的clearerr方法,该方法会重置句柄上的错误和文件结束状态。

     

CPAN还有一个File::Tail模块。