Trance339
Trance339

Reputation: 307

Use of uninitialized value $1 in addition - Perl

I am writing a program that somewhat mimics the last command in UNIX, and I am trying to use backreferencing in my solution. My program does exactly what it is supposed to do but I get a run time error/warning. My question is why is this error/warning coming up and how can I fix an issue like this?

If you need more information I can provide.

Program Execution

./last dodoherty

OUTPUT

Here is a listing of the logins for dodoherty:

1. dodohert pts/1        pc-618-012.omhq. Wed Feb  8 09:19   still logged in
2. dodohert pts/6        ip98-168-203-118 Tue Feb  7 19:19 - 20:50  (01:31)
3. dodohert pts/3        137.48.207.178   Tue Feb  7 14:00 - 15:06  (01:05)
4. dodohert pts/1        137.48.219.250   Tue Feb  7 12:32 - 12:36  (00:04)
5. dodohert pts/21       137.48.207.237   Tue Feb  7 12:07 - 12:23  (00:16)
6. dodohert pts/11       ip98-168-203-118 Mon Feb  6 20:50 - 23:29  (02:39)
7. dodohert pts/9        ip98-168-203-118 Mon Feb  6 20:31 - 22:57  (02:26)
8. dodohert pts/5        pc-618-012.omhq. Fri Feb  3 10:24 - 10:30  (00:05)
Use of uninitialized value $1 in addition (+) at ./odoherty_last.pl line 43.
Use of uninitialized value $2 in addition (+) at ./odoherty_last.pl line 44.
Here is a summary of the time spent on the system for dodoherty:

dodoherty
8
8:6

The Code (Snippet of where the error is coming from, Also this is the only time $1 and $2 are used.)

foreach my $line2 (@user)
{
        $line2 =~ /\S*\((\d{2,2})\:(\d{2,2})\)\s*/;
        $hours = $hours + $1;
        $mins = $mins + $2;

        if( $mins >= 60 )
        {
                $hours = $hours + 1;
                $mins = $mins - 60;
        }
}

Upvotes: 2

Views: 8401

Answers (4)

Bart B
Bart B

Reputation: 669

When ever your RE fails to match, $1 and $2 have no value.

For this reason, it's considered best practice on ever to use $1, $2 etc. inside a conditional which tests the success of the RE.

So don't do:

$string =~ m/(somepattern)/sx;
my $var = $1;

But instead to do something like:

my $var = 'some_default_value';
if($string =~ m/(somepattern)/sx){
  $var = $1;
}

Upvotes: 2

TLP
TLP

Reputation: 67910

As has been noted in other answers, your regex does not match, and therefore $1 and $2 are undefined. It is necessary to always check to make sure the appropriate regex matches before using these variables.

Below I have upgraded your script with some proper perl code. += and %= are handy operator in this case. You can read about them in perlop

Your regex uses \S* and \s*, both of which are completely unnecessary here, since your regex is not anchored to anything else. In other words, \S*foo\s* will match any string that contains foo, since it can match the empty string around foo. Also, {2,2} means "match at least 2 times, max 2", which in effect is the same as {2} "match 2 times".

You will see that I changed your math around, and that is because it assumes that $mins will never be higher than 120. I suppose technically, that is a safe assumption, but doing it like below, it can handle all values of minutes and successfully turn them into hours.

The script below is for demonstration. If you remove DATA and leave <>, you can use this script as-is like so:

last user | perl script.pl

Code:

use strict;
use warnings;
use v5.10;  # required for say()

my ($hours, $mins);

while (<DATA>) {   # replace with while (<>) for live usage
    if (/\((\d{2})\:(\d{2})\)/) {
        $hours += $1;
        $mins  += $2;
        if( $mins >= 60 ) {
            $hours += int ($mins / 60);  # take integer part of division
            $mins  %= 60;                # remove excess minutes
        }
    }
}

say "Hours: $hours";
say "Mins : $mins";

__DATA__
1. dodohert pts/1        pc-618-012.omhq. Wed Feb  8 09:19   still logged in
2. dodohert pts/6        ip98-168-203-118 Tue Feb  7 19:19 - 20:50  (01:31)
3. dodohert pts/3        137.48.207.178   Tue Feb  7 14:00 - 15:06  (01:05)
4. dodohert pts/1        137.48.219.250   Tue Feb  7 12:32 - 12:36  (00:04)
5. dodohert pts/21       137.48.207.237   Tue Feb  7 12:07 - 12:23  (00:16)
6. dodohert pts/11       ip98-168-203-118 Mon Feb  6 20:50 - 23:29  (02:39)
7. dodohert pts/9        ip98-168-203-118 Mon Feb  6 20:31 - 22:57  (02:26)
8. dodohert pts/5        pc-618-012.omhq. Fri Feb  3 10:24 - 10:30  (00:05)

Upvotes: 4

RC.
RC.

Reputation: 28207

#!/usr/bin/perl

use strict;
my $hours = 0;
my $mins  = 0;
my $loggedIn = 0;
while (<STDIN>)
{
        chomp;
        if (/\S*\((\d{2,2})\:(\d{2,2})\)\s*/)
        {
            $hours = $hours + $1;
            $mins  = $mins + $2;
            if($mins >= 60 )
            {
                $hours = $hours + 1;
                $mins = $mins - 60;
            }
        }
        elsif (/still logged in$/)
        {
            $loggedIn = 1;
        }
}

print "Summary: $hours:$mins ", ($loggedIn) ? " (Currently logged in)" : "", "\n";

Upvotes: 3

vmpstr
vmpstr

Reputation: 5211

I think the problem might be in the following line.

1. dodohert pts/1 pc-618-012.omhq. Wed Feb 8 09:19 still logged in

That is because nothing matches the pattern so $1 and $2 are undefined.

Upvotes: 10

Related Questions