Perl: Multiply loops, 1 hash and regex

124 views Asked by At

I got stuck with logic behind loops (while & foreach) and AoH. I have basic knowledge about loops and arrays of hashes, but I can't quite understand how to combine them into 1 single and simple solution. My task is to check regular user's password age, if it is older than n-days (last part is OK for me, I know how to solve it, using GetOptions etc,.).

To accomplish that I figured out a solution:

1 Load file /etc/passwd into script, preform regex search to find out regular users. Regular users in Linux like systems have IDs from 1000 and above, so I use this regex to find out those:

/(\w+)[:]x[:]1[0-9]{3}/

2 Load results of regex serch in to array:

my (@Usernames, %pwdsettings);
while (my $pwdsettings = <$fh2>) {
    if ($pwdsettings =~ /(\w+)[:]x[:]1[0-9]{3}/) {
    $pwdsettings{"Username"} = $1;
    push (@Usernames, \%pwdsettings);
    }
}

3 Preform chage check for every entry in array:

my $pwdsett_dump = "tmp/pwdsett-dump.txt";
...
foreach (@Usernames) {
    system("chage -l $_ > $pwdsett_dump")
}

4 Open $pwdsett_dump and then preform second regex search to get date of last password change. After, load results into existing hash inside array (AoH):

open (my $fh3, "<", $pwdsett_dump) or die "Could not open file '$pwdsett_dump': $!";
while (my $array = <$fh3>) {
    if ($array =~ /^Last\s+password\s+change\s+:\s(\w{3})\s+(\d{2}),\s+(\d{4})/) {
        $pwdsettings{"Month"} = $1;
        $pwdsettings{"Day"} = $2;
        $pwdsettings{"Year"} = $3;
    }
}

But, somewhere it went terribly wrong. My script loads only 1 user in to AoH, second user is never loaded and I get $VAR1->[0].

What I want is to understand how AoH and loops are created in right way.

Full script:

#!/usr/bin/perl

use strict;
use warnings;
use Data::Dumper;

my $pwdsett_dump = "tmp/pwdsett-dump.txt";
my $usernames_dump = "tmp/usernames-dump.txt";
system("cat /etc/passwd > $usernames_dump");
open (my $fh2, "<", $usernames_dump) or die "Could not open file '$usernames_dump': $!";

my (@Usernames, %pwdsettings);
while (my $pwdsettings = <$fh2>) {
    if ($pwdsettings =~ /(\w+)[:]x[:]1[0-9]{3}/) {
    $pwdsettings{"Username"} = $1;
    push (@Usernames, \%pwdsettings);
    }
}

foreach (@Usernames) {
    system("chage -l $_ > $pwdsett_dump")
}
open (my $fh3, "<", $pwdsett_dump) or die "Could not open file '$pwdsett_dump': $!";
while (my $array = <$fh3>) {
    if ($array =~ /^Last\s+password\s+change\s+:\s(\w{3})\s+(\d{2}),\s+(\d{4})/) {
    $pwdsettings{"Month"} = $1;
    $pwdsettings{"Day"} = $2;
    $pwdsettings{"Year"} = $3;
    }
}

print Dumper \@Usernames;
1

There are 1 answers

3
chetangb On BEST ANSWER

you need to append the file when you output meaning use ">>" instead of ">" which will overwrite the file. system("chage -l $_ >> $pwdsett_dump") as you are running it in loop you are overwriting each time the loop executes. Use:

foreach (@Usernames) {
    system("chage -l $_ >> $pwdsett_dump")
}
########sample script
#!/usr/bin/perl

use strict;
use warnings; 

my $usernames_dump = "/etc/passwd";
open (my $fh2, "<", $usernames_dump) or die "Could not open file '$usernames_dump': $!";

my @pwdsettings;
my $i =0;
my @pwdsett_dump;
while (<$fh2>) {

if ($_ =~ /(\w+)[:]x[:]1[0-9]{3}/) {
my @user = split(/:/, $_);
 $pwdsettings[$i] = $user[0];
 $pwdsett_dump[$i] = `chage -l $user[0]|grep Last`;
 $pwdsett_dump[$i] =~ s/Last.*://;
 $pwdsett_dump[$i] =~ s/,//;
 my @m = split(/ /,$pwdsett_dump[$i]);
 print "$user[0]\t Date: $m[2] Month: $m[1] Year: $m[3]\n";
 $i++;
   }
 }
 Output: testuser Date: 12 Month: May Year: 2015