Perl: Sorting a hash value that is a list.

Hi Folks
I am very much a newbie at perl but picking it up and I'm hoping you can help.

I have a file input that details all the /etc/group files in our enterprise in the following format: "<host>:<group>:<gid>:<users>"

I want to parse this data display it as the following: "<group>:<gid>:<users>"

So i can trace which users are members of each group (regardless of the host).

So far I have used a hash and am using <group>:<gid> as my key.

This is my code so far:

#!/usr/bin/perl
use strict;
use warnings;
use Cwd;
my $cwd = cwd;
my $file = $cwd . '/usrgrps.txt';
my $gid;
my $grp;
my $host;
my $group;
my $userid;
my %table = ();
open(FILE, "<", $file) or die "Can't open $file:$!";
while(<FILE>) {
chomp;
($host, $grp, $gid, $userid) = split(/
+:/, $_);
$group = "$grp:$gid";
push @{$table{$group}}, $userid;
}
foreach $group (sort keys %table) {
print "$group:";
my @users = @{$table{$group}};
print join ',', sort @users;
print "\n";
}
 
my $number = values %table; 
print $number . "\n";

This is roughly my input file:

host1:group1:9001:user1,user2,user3,user4,user5
host1:group2:9002:user1,user2,user3
host1:group3:9003:user1,user2,user4
host1:group4:9004:user1,user2,user5
host1:group5:9005:user1,user2
host1:group6:9006:
host2:group1:9001:user1,user2,user3,user4,user5
host2:group2:9002:user1,user2,user3
host2:group3:9003:user1,user2,user4
host2:group4:9004:user1,user2,user5
host2:group5:9005:user1,user2
host2:group1:9006:
host3:group1:9001:user1,user2,user3,user4,user5
host3:group2:9002:user1,user2,user3
host3:group3:9003:user1,user2,user4
host3:group4:9004:user1,user2,user5
host3:group5:9005:user1,user2
host3:group1:9006:

This is what I am getting back:

group1:9001:user1,user2,user3,user4,user5,user1,user2,user3,user4,user5,user1,user2,user3,user4,user5
group2:9002:user1,user2,user3,user1,user2,user3,user1,user2,user3
group3:9003:user1,user2,user4,user1,user2,user4,user1,user2,user4
group4:9004:user1,user2,user5,user1,user2,user5,user1,user2,user5
group5:9005:user1,user2,user1,user2,user1,user2
group6:9006:,,,

My question is, how do I get only unique elements assigned to each key? Any help or advise you can offer will be greatly appreciated. Thanks in advance

can you post your expected output ?
you need to get the unique users for each group ?

Yes, my expected output is a list of unique userid per each group.

Please post a sample of your desired output. Something like.. this is how my output should look!

1 Like

Thanks for quick replies:

This is how my output should look:

group1:9001:user1,user2,user3,user4,user5
group1:9006:
group2:9002:user1,user2,user3
group3:9003:user1,user2,user4
group4:9004:user1,user2,user5
group5:9005:user1,user2
group6:9006:

#!/usr/bin/perl
use strict;
use warnings;

my ($gid, $grp, $host, $group, $userid);
my %table;

open FILE, "< /path/to/usrgrps.txt";
foreach (<FILE>) {
    chomp;
    ($host, $grp, $gid, $userid) = split /:/;
    $group = "$grp:$gid";
    if (length ($userid) != 0) {
        (defined $table{$group} && $table{$group} =~ /$_/) ? next : ($table{$group}.="$_,") foreach (split /,/, $userid);
    }
    else {
        $table{$group} = "";
    }
}
close FILE;

foreach (sort keys %table) {
    $table{$_} =~ s/,$//;
    print "$_:$table{$_}\n";
}
1 Like

Hi,

Try this one,

#!/usr/bin/perl
use strict;
use Cwd;
my $cwd = cwd;
my $file = $cwd . '/usrgrps.txt';
my $gid;
my $grp;
my $host;
my $group;
my $userid;
my %table = ();
open(FILE, "<", $file) or die "Can't open $file:$!";
while(<FILE>)
{
chomp;
($host, $grp, $gid, $userid) = split(/\:/, $_);
$group = "$grp:$gid";
my @users=split(/\,/,$userid);
if ( scalar(@users) )
{
   foreach my $user ( @users )
   {
      push(@{$table{$group}}, $user) if ( ! grep{/$user/} @{$table{$group}} );
   }
}
else
{
   push @{$table{$group}}, $userid;
}
}
foreach $group (sort keys %table) {
print "$group:";
my @users = @{$table{$group}};
print join ',', sort @users;
print "\n";
}

Cheers,
Ranga:)

1 Like

Absolutely first class!!!
You wouldn't believe how many hours I've put in trying to do this.

Works exactly the way I want it to.

I can't thank you enough 'balajesuri'!!!:b:

---------- Post updated at 11:25 AM ---------- Previous update was at 10:22 AM ----------

Don't suppose you know how I would save the output to a file?

The output will stored in outfile.txt. change your outfile name based on your requirement.

Cheers,
Ranga:)

Last few lines edited.

open OUT, "> /path/to/output.txt";
foreach (sort keys %table) {
    $table{$_} =~ s/,$//;
    print OUT "$_:$table{$_}\n";
}
close OUT;