【问题标题】:Get value from next N rows of a file从文件的下 N 行获取值
【发布时间】:2018-08-21 02:46:01
【问题描述】:

我在截取我正在阅读的$lines[0] 上方的行内容时遇到问题,如下foreach 循环

my $IN_DIR  = "/tmp/appo/log";              # Input Directories
my $jumprow = '<number of row to skip>';    # This is a value

foreach my $INPUT ( glob( "$IN_DIR/logrotate_*.log" ) ) {

    open( my $fh, '<', $INPUT ) or die $!;

    while ( <$fh> ) {

        next unless $. > $jumprow;

        my @lines = split /\n/;
        my $i     = 0;

        foreach my $lines ( @lines ) {

            if ( $lines[$i] =~ m/\A#\d.\d.+#\d{4}\s\d{2}\s\d{2}\s\d{2}:\d{2}:\d{2}:\d{3}#\+\d+#\w+#\/\w+\/\w+\/Authentication/ ) {

                # Shows only LOGIN/LOGOUT access type and exclude GUEST users

                if ( $lines[ $i + 2 ] =~ m/Login/ || $lines[ $i + 2 ] =~ m/Logout/ && $lines[ $i + 3 ] !~ m/Guest/ ) {

                    my ( $y, $m, $d, $time ) = $lines[$i] =~ /\A#\d.\d.+#(\d{4})\s(\d{2})\s(\d{2})\s(\d{2}:\d{2}:\d{2}:\d{3})/;

                    my ( $action ) = $lines[ $i + 2 ] =~ /\A(\w+)/;
                    my ( $user )   = $lines[ $i + 3 ] =~ /\w+:\s(.+)/;

                    print "$y/$m/$d;$time;$action;$user\n";
                }
            }
            else {
                next;    # Is this next technically necessary according to you?
            }

            $i++;
        }
    }

    close( $fh );
}

Tie::File 模块可以帮助我

my $IN_DIR  = "/tmp/appo/log";              # Input Directories
my $jumprow = '<number of row to skip>';    # This is a value

foreach my $INPUT ( glob( "$IN_DIR/logrotate_*.log" ) ) {

    tie @lines, 'Tie::File', $INPUT, mode => O_RDONLY;
            or die $!;

    my $i = $.;

    next unless $i > $jumprow;

    foreach my $lines ( @lines ) {

        if ( $lines[$i] =~ m/\A#\d.\d.+#\d{4}\s\d{2}\s\d{2}\s\d{2}:\d{2}:\d{2}:\d{3}#\+\d+#\w+#\/\w+\/\w+\/Authentication/ ) {

            # Shows only LOGIN/LOGOUT access type and exclude GUEST users

            if ( $lines[ $i + 2 ] =~ m/Login/ || $lines[ $i + 2 ] =~ m/Logout/ && $lines[ $i + 3 ] !~ m/Guest/ ) {

                my ( $y, $m, $d, $time ) = $lines[$i] =~ /\A#\d.\d.+#(\d{4})\s(\d{2})\s(\d{2})\s(\d{2}:\d{2}:\d{2}:\d{3})/;

                my ( $action ) = $lines[ $i + 2 ] =~ /\A(\w+)/;
                my ( $user )   = $lines[ $i + 3 ] =~ /\w+:\s(.+)/;

                print "$y/$m/$d;$time;$action;$user\n";
            }
        }
        else {
            next;    # Is this next technically necessary according to you?
        }

        $i++;
    }
}

你能告诉我我在Tie::File 的声明是否正确吗? 这只是我的主脚本的一部分,如以下指南中所示 mcve

实际上没有tie,我的主脚本只适用于$lines[0],它不会从$lines[$i+2]$lines[$i+3]中获取价值

【问题讨论】:

  • 能否也包括示例输入和预期输出?
  • @simbabque Hi :) 输入文件是不是和这个stackoverflow.com/q/51636352/1195443一样呢
  • 输出为 "$y/$m/$d;$time;$action;$user\n";
  • Dave 是否真的解决了您刚刚链接的最后一个问题中的问题?如果是这样,您可能想接受他的回答,或者告诉我们它是如何没有帮助的。我们确实关注这里的这些事情,如果您接受他们的免费帮助但不给予他们信任,我们中的一些人会感到不受欢迎。 ;)
  • 请向我们展示 actual 文字输出。我不知道这些变量中有什么。

标签: perl tie


【解决方案1】:

看来您在这里迷路了。我编写了一个工作程序来处理您在上一个问题中显示的数据;它至少应该为您继续工作奠定稳定的基础。我认为这相当简单,但请询问 Perl 文档中是否有任何不明显的内容

use strict;
use warnings 'all';
use feature 'say';
use autodie;  # Handle IO failures automatically

use constant IN_DIR => '/tmp/appo/log';

chdir IN_DIR; # Change to input directory
              # Status handled by autodie

for my $file ( glob 'logrotate_*.log' ) {

    say $file;
    say '-' x length $file;
    say "";

    open my $fh, '<', $file; # Status handled by autodie

    local $/ = "";           # Enable block mode

    while ( <$fh> ) {

        my @lines = split /\n/;

        next unless $lines[0] =~ /
            ^
            \# \d.\d .+?
            \# (\d\d\d\d) \s (\d\d) \s (\d\d)
            \s
            ( \d\d : \d\d : \d\d : \d\d\d )
        /x;
        my ( $y, $m, $d, $time ) = ($1, $2, $3, $4);
        $time =~ s/.*\K:/./;  # Change decimal point to dot for seconds

        next unless $lines[2] =~ /^(Log(?:in|out))/;
        my $action = $1;

        next unless $lines[3] =~ /^User:\s+(.*\S)/ and $1 ne 'Guest';
        my $user = $1;

        print "$y/$m/$d;$time;$action;$user\n";
    }

    say "";
}

输出

logrotate_0.0.log
-----------------

2018/05/24;11:05:04.011;Login;USER4
2018/05/24;11:04:59.410;Login;USER4
2018/05/24;11:05:07.100;Logout;USER3
2018/05/24;11:07:21.314;Login;USER2
2018/05/24;11:07:21.314;Login;USER2
2018/05/26;10:48:02.458;Logout;USER2
2018/05/28;10:00:25.000;Logout;USER0

logrotate_1.0.log
-----------------

2018/05/29;10:09:45.969;Login;USER4
2018/05/29;11:51:06.541;Login;USER1
2018/05/30;11:54:03.906;Login;USER4
2018/05/30;11:59:59.156;Logout;USER3
2018/05/30;08:32:11.348;Login;USER4
2018/05/30;11:09:54.978;Login;USER2
2018/06/01;08:11:30.008;Logout;USER2
2018/06/01;11:11:29.658;Logout;USER1
2018/06/02;12:05:00.465;Logout;USER9
2018/06/02;12:50:00.065;Login;USER9
2018/05/24;10:43:38.683;Login;USER1

【讨论】:

  • 感谢您的回答非常翔实,无疑是解决方案,我立即运行测试以将您的逻辑集成到脚本中
  • @clarkseth:应该没有什么可以“整合”的。这段代码应该是一个完整的解决方案。它可以处理所有版本的代码。
  • 我在这个解决方案中加入了跳线法
  • @clarkseth:记录上次读取的日期/时间会好得多,并且在下次读取同一文件时忽略小于该日期/时间的块。这相当于while 循环中的另一个next unless,以及用于存储和检索最近块中的日期/时间的代码。也许您甚至可以使用您已经在编写的文件并从中读取最后一条记录?
  • @clarkseth:您可能希望将您的代码提交给Code Review,因为您显然还不熟悉 Perl,您可能会从那里的反馈中学到很多东西。这尤其适用于您不愿透露自己所写内容的情况!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-26
  • 1970-01-01
  • 2020-12-12
  • 2019-07-22
  • 1970-01-01
  • 2011-11-08
相关资源
最近更新 更多