in reply to Re: Loop problem: jumps one record (updated)
in thread Loop problem: jumps one record

Thank you for your input ! I just started learning Perl yesterday, so the "state machine type approach" is still new to me. I just want to add that I need to store the $2 in a variable because I then add it to a database. I also run into another problem : the loop skips the second round of entries as there are a bunch of "/Relay.access.denied/" in the file. Which I'm guessing is because the "<>" already goes through it at the end of the nested loop. So my question is, is there a way to tell <> to go back one line at the end of the loop.
  • Comment on Re^2: Loop problem: jumps one record (updated)

Replies are listed 'Best First'.
Re^3: Loop problem: jumps one record (updated)
by Discipulus (Canon) on Jan 31, 2017 at 12:39 UTC
    is there a way to tell <> to go back one line at the end of the loop is the wrong question agent Spooner..

    Why you want to force an iterator to go back?

    Another approach is better: something is true after Relay access denied is encountered and become false when Sender address rejected is reached (hoping thi is your case).

    Perl offer you a funny named flip-flop operator (see my recent post about it for links and explaination).

    The following short program it is nothing more that: if we are between a START and STOP sentence, print the IP if you find an IP.

    use strict; use warnings; while (<DATA>){ if (/^Relay access denied/ .. /Sender address rejected/){ print "$2\n" if $_ =~ /(\d+)\s+(\S+)/; } } __DATA__ Relay access denied (total: 2) 1 111.111.111.111 1 222.222.222.222 1 333.333.333.333 Sender address rejected: Access denied (total: 50) 1 200.200.200.200 Relay access denied (total: 1) 1 255.255.255.255 Sender address rejected: Access denied (total: 50) ##OUTPUT 111.111.111.111 222.222.222.222 333.333.333.333 255.255.255.255

    As you posted as anonymous your first post i missed the opportunity to express you a warm welcome to the monastery and to the wonderful world of Perl math&ing001 !

    In addition the special token DATA is very useful to embed some data example to your program: it is described in perldata in the section Special Literals

    Regexp::Common::net is a convenient module to match IPs: you see above the regex match 333.333.333.333 as valid IP. As side note do not put real IP data on the Net: use always fake one as iI did.

    L*

    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
Re^3: Loop problem: jumps one record
by haukex (Archbishop) on Jan 31, 2017 at 12:26 UTC

    Hi math&ing001,

    I also run into another problem : the loop skips the second round of entries as there are a bunch of "/Relay.access.denied/" in the file. ... So my question is, is there a way to tell <> to go back one line at the end of the loop.

    There isn't a good way to do that, however, in the code examples I showed, it is of course possible to do multiple things on each line of input, which is how I would approach the solution. However, it's hard for me to fully understand the problems you described, it would be helpful if you could provide more short sample input that illustrates the problem along with the output you are expecting for that sample input. See also Short, Self-Contained, Correct Example and How do I post a question effectively?

    Regards,
    -- Hauke D

      Hi Hauke, This is an example of the file format:
      blocked using dummy1 (total: 6) 3 rzxwrvxk.com 1 correio.biz 1 facebook.com 1 213.183.58.6 blocked using dummy2 (total: 330) 2 118.125.110.201 1 61.2.46.20 blocked using dummy3 (total: 5) 2 hinet.net 1 219.140.15.195 1 219.139.16.134 1 222.189.112.23 blocked using dummy4 (total: 53) 5 66.23.212.67 5 66.23.212.70 4 66.23.212.68 4 66.23.212.69 Relay access denied (total: 13) 1 46.183.217.174 1 46.183.220.137 1 46.183.220.138
      Code:
      use strict; use warnings; my $ip=""; my $io; while (<>) { if (/blocked.using/) { do { $io = <>; $ip = $2 if $io =~ /(\d+)\s+(\S+)/; print $ip; #adding to database; } until ($io !~ /(\d+)\s+(\S+)/); } if (/Relay.access.denied/) { do { $io = <>; $ip = $2 if $io =~ /(\d+)\s+(\S+)/; print $ip; # adding to database; } until ($io !~ /(\d+)\s+(\S+)/); } }
      Output I get :
      rzxwrvxk.com correio.biz facebook.com 213.183.58.6 213.183.58.6 hinet.net 219.140.15.195 219.139.16.134 222.189.112.23 222.189.112.23 46.183.217.174 46.183.220.137 46.183.220.138

        Hi math&ing001,

        When I run your code against your sample input, it does not match the output you gave; also you didn't say what output you expect - the more precise you are in your questions, the better we are able to help :-)

        What I see happening is that I get stuff like "(total:" in the output, the reason is that the regex /(\d+)\s+(\S+)/ also matches the string "blocked using dummy3 (total: 5)". One way to avoid this would be to be more specific in your regexes. One simple fix would be to write your regexes as /^\s*(\d+)\s+(\S+)\s*$/.

        Anyway, the first thing I would suggest is that you don't use do { } until () loops, and instead use while loops, and then use the last command to break out of the loop under certain conditions, this will give you more precise control. For example, the first of your inner loops could be written like this:

        if (/blocked.using/) { while (<>) { /^\s*(\d+)\s+(\S+)\s*$/ or last; $ip = $2; print "$ip\n"; } }

        As for the state machine approach:

        use warnings; use strict; use constant { IDLE => 0, RELAY => 1, BLOCKED => 2, }; my $state = IDLE; while (<>) { chomp; if (/Relay.access.denied/) { $state = RELAY; } elsif (/blocked.using/) { $state = BLOCKED; } elsif (/^\s*(\d+)\s+(\S+)\s*$/) { if ($state == RELAY) { print "Relay Access Denied: $2\n" } elsif ($state == BLOCKED) { print "Blocked: $2\n" } } else { $state = IDLE; } }

        Hope this helps,
        -- Hauke D

        Just use the section name as the state flag...

        #!/usr/bin/perl # http://perlmonks.org/?node_id=1180665 use strict; use warnings; my $section = ''; while(<DATA>) { if( /(.*)\(total: \d+\)/ ) # get section name { $section = $1; } elsif( $section =~ /Relay access denied/ and /^\s*\d+\s+(\S+)\s*$/ ) { print "$section $1\n"; # add $1 to DB one way } elsif( $section =~ /blocked using/ and /^\s*\d+\s+(\S+)\s*$/ ) { print "$section $1\n"; # add $1 to DB a different way } } __DATA__ Relay access denied (total: 13) 1 46.183.217.174 1 46.183.220.137 1 46.183.220.138 1 46.183.220.139 1 46.183.223.239 1 218.38.243.204 1 185.29.8.198 1 185.29.9.133 1 185.29.9.135 1 46.183.217.162 1 46.183.217.165 1 46.183.217.169 1 91.236.75.169 Sender address rejected: Access denied (total: 50) 1 77.236.75.169 blocked using dummy1 (total: 6) 3 rzxwrvxk.com 1 correio.biz 1 facebook.com 1 213.183.58.6 blocked using dummy2 (total: 330) 2 118.125.110.201 1 61.2.46.20 blocked using dummy3 (total: 5) 2 hinet.net 1 219.140.15.195 1 219.139.16.134 1 222.189.112.23 blocked using dummy4 (total: 53) 5 66.23.212.67 5 66.23.212.70 4 66.23.212.68 4 66.23.212.69 Relay access denied (total: 13) 1 46.183.217.174 1 46.183.220.137 1 46.183.220.138

        What is difference between the 2 #adding to database sections, are they updating different tables ?

        poj