http://qs321.pair.com?node_id=1176613

valerydolce has asked for the wisdom of the Perl Monks concerning the following question:

Hi folks - The code below is not working as expected. It supposed to grab the file name from the input and open the file. It populates "CAN't open the file" even when i enter the right filename. Please assist.
use strict; use warnings; my $theFile; print "Which file among the following files do you want to check ? \n" +; my $directory = '/home/psimo/it441/challenge/logs'; + opendir (DIR, $directory) or die $!; + + while ( my $file = readdir(DIR) ) { + print " $file \n"; + + } print "Please enter the file name: "; my $theFile = <STDIN>; chomp ($theFile); open(FH, '<', '/home/psimo/it441/challenge/logs/$theFile') or die "CAN +'T OPEN FILE! you don't have the log file\n";

Replies are listed 'Best First'.
Re: Grab input from the user and Open the file
by shmem (Chancellor) on Nov 26, 2016 at 19:18 UTC
    my $directory = '/home/psimo/it441/challenge/logs'; ... open(FH, '<', '/home/psimo/it441/challenge/logs/$theFile') or die "CAN +'T OPEN FILE! you don't have the log file\n";

    Since you have the directory hardcoded, you should say:

    open FH, '<', "$directory/$theFile" or die "CAN'T OPEN FILE! you don't + have the log file\n";

    Note the usage of double quotes in my suggestion. Single quotes take $theFile as literal, while double quotes interpolate the variables content.

    perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
Re: Grab input from the user and Open the file
by Cristoforo (Curate) on Nov 26, 2016 at 19:21 UTC
    You need double quotes to interpolate "/home/psimo/it441/challenge/logs/$theFile" or die ....
Re: Grab input from the user and Open the file
by AnomalousMonk (Archbishop) on Nov 26, 2016 at 22:11 UTC

    You could have gained some insight into the basic problem (single-quotes don't interpolate) if you had put the file path and name, delimited to show any leading or trailing whitespace, into the error message — and the error  $! too!:

    my $directory = '/home/psimo/it441/challenge/logs'; ... my $theFile = ...; ... my $path_file = "$directory/$theFile"; open my $fh, '<', $path_file or die "opening '$path_file': $!"; ...
    (I'm also using a lexical filehandle rather than a global one, another good practice.)


    Give a man a fish:  <%-{-{-{-<

      Hi guys,

      I have another concern with the code below which actually prints the following:

      Nov 27 04:08:34 vm73 sshd[14302]: Address 173.254.240.68 maps to 173. +254.240.68.static.quadranet.com, but this does not map back to the ad +dress - POSSIBLE BREAK-IN ATTEMPT! Nov 27 04:08:37 vm73 sshd[14304]: Address 173.254.240.68 maps to 173.2 +54.240.68.static.quadranet.com, but this does not map back to the add +ress - POSSIBLE BREAK-IN ATTEMPT! Nov 27 04:08:40 vm73 sshd[14306]: Address 173.254.240.68 maps to 173.2 +54.240.68.static.quadranet.com, but this does not map back to the add +ress - POSSIBLE BREAK-IN ATTEMPT! Nov 27 04:08:42 vm73 sshd[14308]: Address 173.254.240.68 maps to 173.2 +54.240.68.static.quadranet.com, but this does not map back to the add +ress - POSSIBLE BREAK-IN ATTEMPT! Nov 27 04:08:45 vm73 sshd[14310]: Address 173.254.240.68 maps to 173.2 +54.240.68.static.quadranet.com, but this does not map back to the add +ress - POSSIBLE BREAK-IN ATTEMPT! Nov 27 04:08:48 vm73 sshd[14312]: Address 173.254.240.68 maps to 173.2 +54.240.68.static.quadranet.com, but this does not map back to the add +ress - POSSIBLE BREAK-IN ATTEMPT! Nov 27 04:08:51 vm73 sshd[14314]: Address 173.254.240.68 maps to 173.2 +54.240.68.static.quadranet.com, but this does not map back to the add +ress - POSSIBLE BREAK-IN ATTEMPT! Nov 27 04:08:54 vm73 sshd[14316]: Address 173.254.240.68 maps to 173.2 +54.240.68.static.quadranet.com, but this does not map back to the add +ress - POSSIBLE BREAK-IN ATTEMPT! Nov 27 04:08:56 vm73 sshd[14318]: Address 173.254.240.68 maps to 173.2 +54.240.68.static.quadranet.com, but this does not map back to the add +ress - POSSIBLE BREAK-IN ATTEMPT! Nov 27 04:08:59 vm73 sshd[14324]: Address 173.254.240.68 maps to 173.2 +54.240.68.static.quadranet.com, but this does not map back to the add +ress - POSSIBLE BREAK-IN ATTEMPT! Nov 27 04:09:02 vm73 sshd[14328]: Address 173.254.240.68 maps to 173.2 +54.240.68.static.quadranet.com, but this does not map back to the add +ress - POSSIBLE BREAK-IN ATTEMPT! Nov 27 04:09:05 vm73 sshd[14330]: Address 173.254.240.68 maps to 173.2 +54.240.68.static.quadranet.com, but this does not map back to the add +ress - POSSIBLE BREAK-IN ATTEMPT! Nov 27 04:09:08 vm73 sshd[14333]: Address 173.254.240.68 maps to 173.2 +54.240.68.static.quadranet.com, but this does not map back to the add +ress - POSSIBLE BREAK-IN ATTEMPT! Nov 27 04:09:11 vm73 sshd[14335]: Address 173.254.240.68 maps to 173.2 +54.240.68.static.quadranet.com, but this does not map back to the add +ress - POSSIBLE BREAK-IN ATTEMPT! Nov 27 04:09:13 vm73 sshd[14337]: Address 173.254.240.68 maps to 173.2 +54.240.68.static.quadranet.com, but this does not map back to the add +ress - POSSIBLE BREAK-IN ATTEMPT! Nov 27 04:09:21 vm73 sshd[14339]: Address 173.254.240.68 maps to 173.2 +54.240.68.static.quadranet.com, but this does not map back to the add +ress - POSSIBLE BREAK-IN ATTEMPT! Nov 27 04:09:25 vm73 sshd[14342]: Address 173.254.240.68 maps to 173.2 +54.240.68.static.quadranet.com, but this does not map back to the add +ress - POSSIBLE BREAK-IN ATTEMPT! Nov 27 04:09:28 vm73 sshd[14344]: Address 173.254.240.68 maps to 173.2 +54.240.68.static.quadranet.com, but this does not map back to the add +ress - POSSIBLE BREAK-IN ATTEMPT! Nov 27 04:09:30 vm73 sshd[14346]: Address 173.254.240.68 maps to 173.2 +54.240.68.static.quadranet.com, but this does not map back to the add +ress - POSSIBLE BREAK-IN ATTEMPT! Nov 28 04:58:11 vm73 sshd[1882]: Address 198.144.186.184 maps to host. +colocrossing.com, but this does not map back to the address - POSSIBL +E BREAK-IN ATTEMPT!

      How do i modify this code so that it can determine the number of time that an IP address occurred and the domain name from which the IP is mapped ?

      my $ipFound; my $count = 0; print "Which file among the following files do you want to check ? \n" +; my $directory = '/home/psimo/it441/challenge/logs'; opendir (DIR, $directory) or die $!; while ( my $file = readdir(DIR) ) { print " $file \n"; } print "Please enter the file name: "; my $theFile = <STDIN>; chomp ($theFile); open READ, '<', "$directory/$theFile" or die "CAN'T OPEN FILE! The fil +e name entered does not exist\n"; while(<READ>){ if(/POSSIBLE BREAK-IN ATTEMPT!/){ if(/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/){ $ipFound = $1; print $_; $count++; } } } print " There are $count break-in attempts ";

        Hi valerydolce,

        How do i modify this code so that it can determine the number of time that an IP address occurred and the domain name from which the IP is mapped?

        For both, you can use a hash or two, depending on whether you want to keep things simple or dive into a hash of hashes data structure. The simple approach would be to declare a hash like my %seencount; before the loop, and then increment the seen counter for each IP by adding $seencount{$ipFound}++; in the loop. Similarly for the hostname, if you make the assumption that each IP maps to one hostname you can create a new hash and simply say $hostnames{$ipFound} = $hostname; to store it. Of course, that will require you to get the hostname from the string, which you can do with either the same regex as the one for getting the IP, or a new one. For more on hashes, see for example "Hashes" in Modern Perl or perldata for all the details, and for more on regexes see perlrequick, perlretut, or for all the details perlre. Or, you can use $RE{net}{domain} from Regexp::Common::net.

        Anyway, it looks like you're parsing an authentication log. As it happens I wrote a quick script to do the same a while ago, it's available under the GPL here, it does more than you need but maybe you can borrow some of that code.

        Hope this helps,
        -- Hauke D

        (Note: haukex, a faster typist than I, has already covered most of the points below, but I would just want to emphasize the fact that the regex in your code does match invalid IP addresses.)

        Basically, you want to "associate" (hint, hint) a string that represents a "dotted decimal" IP address with a domain name and a count. The data structure (see perldsc) for this might look like

        my %IP = ( # create and initialize '198.144.186.184' => { 'domain' => 'host.colocrossing.com', 'count' => 1, } ); ... # add to structure my $captured_IP = ...; my $captured_domain = ...; $IP{$captured_IP}{domain} = $captured_domain; $IP{$captured_IP}{count}++; ... # print structure -- see perldsc

        Of course, you must already have captured an IP and a domain name from a "POSSIBLE BREAK-IN ATTEMPT!" record, which you seem to be able to identify. It's always best to use what's tried and tested: CPAN.

        The module Regexp::Common::net (but first see Regexp::Common for how to use Regexp::Common::net) contains regexes for matching various forms of IP address. Note that the regex pattern you're using,
            /(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/
        allows a match with, e.g., '999.999.999.999' or '99999.1.2.99999': the first is plain invalid; the second contains a possibly valid IP – or does it?. A better regex can be built using Regexp::Common::net, maybe something like:

        use Regexp::Common qw(net); ... my $dotted_decimal_ip = qr{ (?<! \d) $RE{net}{IPv4} (?! \d) }xms; my $domain_name = qr{ ... }xms; ... my ($break_in_ip) = $break_in_message =~ m{ ($dotted_decimal_ip) } +xms; my ($break_in_domain) = $break_in_message =~ m{ ($domain_name) }xms; $IP{$break_in_ip}{domain} = $break_in_domain; $IP{$break_in_ip}{count}++; ...
        Note that this approach only captures the most recent domain name associated with a particular IP address. I've been a bit vague about the domain-name regex. Such a regex can be quite involved if it must cover all possible domain names, but you may not need anything like this level of coverage. I leave it to you to search CPAN for an appropriate module.


        Give a man a fish:  <%-{-{-{-<

Re: Grab input from the user and Open the file
by Lotus1 (Vicar) on Nov 28, 2016 at 18:16 UTC

    In addition to the excellent replies above I would like to mention file tests. While it is fine to attempt to open the file and then print out any errors, you can also test the file before you attempt to open it. If the file doesn't exist you could prompt the user to resubmit in case of a typo.

    use warnings; use strict; my $directory = '/home/psimo/it441/challenge/logs'; + print "Please enter the file name: "; my $theFile = <STDIN>; chomp ($theFile); my $path = "$directory/$theFile"; if( ! -f $path ) { print "$path isn't a file.\n"; } else { print "$path is a file.\n"; } if( -T $path ) { print "$path is a text file.\n"; } else { print "$path is not a text file.\n"; }
      my ($theFile); until (-f $theFile) { print "Please enter the file name:"; chomp($theFile = <STDIN>); }
      But God demonstrates His own love toward us, in that while we were yet sinners, Christ died for us. Romans 5:8 (NASB)

Re: Grab input from the user and Open the file
by valerydolce (Novice) on Nov 27, 2016 at 04:23 UTC
    Thanks Gentlemen. I appreciate your help.