perltie

Get value from next N rows of a file


I'm having problems intercepting the contents of the lines above what I'm reading $lines[0] as following foreach loop

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 );
}

The Tie::File module could help me

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++;
    }
}

Could you tell me if my declaration with Tie::File is correct or not? This is only a part of my master script as indicated in following guide mcve

Actually without tie, my master scripts works only with $lines[0], it doesn't take value from $lines[$i+2] or $lines[$i+3]


Solution

  • It looks like you're getting very lost here. I've written a working program that processes the data you showed in your previous question; it should at least form a stable basis for you to continue your work. I think it's fairly straightforward, but ask if there's anything that's not obvious in the Perl documentation

    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 "";
    }
    

    output

    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