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

I have several boxes I am trying to cleanup, do audits on via /etc/passwd which is a : delimited file so i use split and a hash to get only the unique ids back, so if they are on several severs at once it gets lost in the reiteration, what I am wondering is 2 things how do i make the hash multidimensional one by pulling out whatever i need off of the passwd files from 9 or so different servers and like uid and gcos info (actually I already wrote this code-seen below)and two being able to tell exactly what info came from what passwd files like from what different servers. I have the passwd files named like this passwd.server1, passwd.server2,etc... I also want to know how to make the passwd array wildcarded in the passing so as not to have to write out each passwd file in the array itself, since they all begin with passwd anyway. Below is a working copy, for me anyway, of my code so far:

#! /usr/bin/perl @files=('passwd.server1','passwd.server2','passwd.server3','pa +sswd.server4', 'passwd.server5','passwd.server6','passwd.server7','passwd.server8 +','passwd.server9'); foreach $file (@files){ open (PASSWD,"$file"); $nf="endo"; open (NEWFILE, ">$nf"); while (<PASSWD>) { ($login, $passwd, $uid, $gid, $gcos, $home, $shell) = split(/:/); $USERS{$login} = $gcos; } close (PASSWD); } + foreach $login (sort keys %USERS) { $gcos = $USERS{$login}; print NEWFILE "$login, $gcos\n"; }

Thanks for your help!!
Tux242

Edited 2003-10-30 by Ovid

Title edit by tye

Replies are listed 'Best First'.
Re: /etc/passwd
by Limbic~Region (Chancellor) on Oct 30, 2003 at 22:09 UTC
    tux242,
    What you are looking for is most likely a Hash of Hashes of Arrays (HoHoA).
    Consider the following untested code:
    #!/usr/bin/perl -w use strict; use Data::Dumper; my @files = map { 'passwd.server' . $_ } 1 .. 9; my %data; for my $file (@files) { open (PASSWD, $file) or die "Unable to open $file : $!"; while (<PASSWD>) { chomp; my @field = split /:/ , $_; $data{$file}{$field[0]} = \@field } } # Code to manipulate and identify conflicts in %data goes here print Dumper(\%data);
    I hope this helps. If I knew what sort of "clean up" you were trying to do, I would have done more.

    Cheers - L~R

Re: /etc/passwd
by jasonk (Parson) on Oct 30, 2003 at 22:20 UTC

    Using a wildcard for the filename is easy enough, just use the glob() operator... As for the other part of the question, I'm not sure what you are asking but it sounds like you want to keep track of which users had accounts on which machines. I'd probably do the whole thing something like this:

    #!/usr/bin/perl -w use strict; my %USERS; foreach my $file (<passwd.server*>) { open(PASSWD,$file); while(<PASSWD>) { my($login,$gcos) = (split(':',$_))[0,4]; if(exists $USERS{$login}) { push(@{$USERS{$login}},$file); } else { $USERS{$login} = [$gcos,$file]; } } close(PASSWD); } open(NEWFILE,">endo"); foreach my $login (sort keys %USERS) { print NEWFILE "$login:".shift(@{$USERS{$login}}).":"; print NEWFILE join(',',@{$USERS{$login}})."\n"; } close(NEWFILE);

    We're not surrounded, we're in a target-rich environment!
Re: /etc/passwd
by Zaxo (Archbishop) on Oct 31, 2003 at 00:32 UTC

    See Perl's getpwent, setpwent, endpwent. They know how to manage shadowed passwords, etc. You don't need to manage the /etc/passwd file at all with those perl functions.

    After Compline,
    Zaxo

Re: /etc/passwd
by pzbagel (Chaplain) on Oct 30, 2003 at 22:16 UTC

    How about some <> magic:

    #! /usr/bin/perl #Call the scripts with <scriptname> passwd.* while (<>) { ($login, $passwd, $uid, $gid, $gcos, $home, $shell) = split(/:/); $USERS{$login} = $gcos; } $nf="endo"; open (NEWFILE, ">$nf"); foreach $login (sort keys %USERS) { $gcos = $USERS{$login}; print NEWFILE "$login, $gcos\n"; }

    Now just call the script with the passwd files as arguments(or even with passwd.*). The <> in the while will automatically open each file in turn and read it in for you. I also moved the opening of NEWFILE to the end of the script since that's when you actually have to have it open.

    Later

Re: /etc/passwd
by sauoq (Abbot) on Oct 31, 2003 at 00:35 UTC

    It looks like you've gotten some help, but no one told you that you don't need to open /etc/passwd, read each line, and split like that...

    Something like this would do it:

    $USERS{ $pwent[ 0 ] } = $pwent[ 4 ] while @pwent = getpwent();

    perldoc -f getpwent

    Update: Sigh. Zaxo beat me to it by a couple of minutes.

    -sauoq
    "My two cents aren't worth a dime.";
    
Re: /etc/passwd
by hardburn (Abbot) on Oct 30, 2003 at 21:46 UTC

    Your question isn't clear at all, but I suspect that a search on CPAN for passwd will find a module that will do what you want.

    ----
    I wanted to explore how Perl's closures can be manipulated, and ended up creating an object system by accident.
    -- Schemer

    : () { :|:& };:

    Note: All code is untested, unless otherwise stated

      3/4 of the code is already written i am a newbie i just need to be able to tell all the passwd.server files each unique id are on and howto shorten the passwd.server array statement in the code given with a wildcard, if possible, so the hash array does not have to be so verbose? Thanks. Tux242
        Ok, I think we can solve both of those problems with the same thing. Try something like this:
        #!/usr/bin/perl $nf="endo"; foreach $file (@ARGV) { open (PASSWD, "$file") || die "Couldn't open $file\n"; while (<PASSWD>) { ($login, $passwd, $uid, $gid, $gcos, $home, $shell) = split(/:/); $USERS{${file}.${login}} = $gcos; #this way the first key is "passwd.server1.root" } close (PASSWD); } open (NEWFILE, ">$nf"); foreach $login (sort keys %USERS) { print NEWFILE "$login, $USERS{$login}\n"; } close (NEWFILE); # just because I'm tidy

        and now you call your script with script.pl passwd*