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

What is the best way to make an array from a field within another array. I can split & iterate though, and get it working, but you folks often have a slicker way. your help is appreciated. Sorry for the unclear post. First, what I wish to accomplish is to locate unused entries in UNIX /etc/group files. This requires the fourth group file field to have no entries, and the second field to not appear in any fourth field of the /etc/passwd file. What I have so far handle the first part, but the second part is not working yet, ( and I need to negate the second part once it works.)
#!/usr/bin/perl -w #<UnusedGrp.pl> Find unused Groups in /etc/group my $GROUPfile="/etc/group"; my $gfpid = open(GFILE, "<$GROUPfile") or die "$GROUPfile File Not Fou +nd: $!\n"; my $PASSWD="/etc/passwd"; my $pfpid = open(PFILE, "<$PASSWD") or die "$PASSWD File Not Found: $! +\n"; @PWLINES = <PFILE>; while (my $line = <GFILE>) { @GRP = split(/:/, $line); if (length($GRP[3]) < 2){ print "$GRP[0]:$GRP[2] is zero length\n"; if ((@GREPED = grep(/\:\*\:${GRP[3]}\:/, @PWLINES))) { print @GREPED; } undef @GREPED; } }

Replies are listed 'Best First'.
Re: Make array from field of another array
by toolic (Bishop) on Apr 07, 2011 at 15:15 UTC
    Slices
    use warnings; use strict; use Data::Dumper; my @all_nums = 0 .. 9; my @few_nums = @all_nums[4 .. 6]; print Dumper(\@few_nums); __END__ $VAR1 = [ 4, 5, 6 ];

    See also grep

Re: Make array from field of another array
by kennethk (Abbot) on Apr 07, 2011 at 15:13 UTC
    Your spec is ill defined - please read I know what I mean. Why don't you?. Specifically, please give us concrete examples of input and output. You say you "can split & iterate though, and get it working", so the easiest way to help us understand what you need is to post code the does what you need. From there, we can critique the code and help you improve your skill with and understanding of Perl.

    Update: With your updated node, I can provide some more useful suggestions. You can accomplish your mapping using the code posted in Limbic~Region's subthread, so the smallest mod to your script the produces your desired result might look like:

    #!/usr/bin/perl -w #<UnusedGrp.pl> Find unused Groups in /etc/group my $GROUPfile="/etc/group"; my $gfpid = open(GFILE, "<$GROUPfile") or die "$GROUPfile File Not Fou +nd: $!\n"; my $PASSWD="/etc/passwd"; my $pfpid = open(PFILE, "<$PASSWD") or die "$PASSWD File Not Found: $! +\n"; @PWLINES = <PFILE>; while (my $line = <GFILE>) { @GRP = split(/:/, $line); if (length($GRP[3]) < 2){ print "$GRP[0]:$GRP[2] is zero length\n"; my @GIDs = map { (split /:/)[3] } @PWLINES; my $seen = 0; for my $GID (@GIDs) { if ($GID == $GRP[2]) { $seen = 1 } } if (not $seen) { print "$GRP[0]:$GRP[2] has no users\n"; } } }

    However, I would approach this problem a little differently. Given that you want to repeatedly check against the presence of a foreign key, the best approach here is Perl is usually using a hash. I would also use next for Loop Control rather than nested conditionals. My solution would look more like:

    #!/usr/bin/perl -w #<UnusedGrp.pl> Find unused Groups in /etc/group use strict; my $GROUPfile="/etc/group"; my $gfpid = open(GFILE, "<", $GROUPfile) or die "$GROUPfile Open faile +d: $!\n"; my $PASSWD="/etc/passwd"; my $pfpid = open(PFILE, "<", $PASSWD) or die "$PASSWD Open failed: $!\ +n"; my %gids; while (my $line = <PFILE>) { my @user = split /:/, $line; $gids{$user[3]}++; } while (my $line = <GFILE>) { my @GRP = split(/:/, $line); if (length $GRP[3] > 1) { # $GRP[0]:$GRP[2] is not zero length next; } if ($gids{$GRP[2]}) { # GID found in passwd next; } print "$GRP[0] has no users and no GID references\n" }

    though my final solution would probably be

    #!/usr/bin/perl -w #<UnusedGrp.pl> Find unused Groups in /etc/group use strict; my $GROUPfile="/etc/group"; my $PASSWD="/etc/passwd"; open my $gh, "<", $GROUPfile or die "$GROUPfile Open failed: $!\n"; open my $ph , "<", $PASSWD or die "$PASSWD Open failed: $!\n"; my %gids = map { (split /:/)[3] => 1 } <$ph>; while (<$gh>) { my @fields = split /:/; next if length $fields[3] > 1; next if $gids{$fields[2]}; print "$fields[0] has no users and no GID references\n" }

    I'll be happy to go over any questions you have about the above code. In particular, see Use strict warnings and diagnostics or die (or The strictures, according to Seuss).

Re: Make array from field of another array
by Limbic~Region (Chancellor) on Apr 07, 2011 at 15:22 UTC
    Saved,
    I agree that you have done a poor job of explaining. Because you have indicated you can accomplish the job using split, I will assume you have some type of delimited data. That might simply look like:
    my @new_array = split /:/, $old_array[0];
    Then you go on to say iterate which makes me think you want the new array to be the result of all the elements of the old array:
    my @new_array = map {split /:/, $_} @old_array;
    You might also mean something completely different so I would recommend you provide an example of how you are doing it now that you don't care for and see if we can improve upon that.

    Cheers - L~R

      your Code:
      my @new_array = map {split /:/, $_} @old_array;
      Is nearly what I was hoping for, but could you do something to get only one field?
      my @new_array = map {split /:/, ${_[3]}} @old_array;
        You can access only one element of the split result by using a list index.

        my @new_array = map { (split /:/)[3] } @old_array;

        Note that I have relied on split's default of splitting $_ when not provided with an explicit string. So the above is exactly equivalent to

        my @new_array = map { (split /:/, $_)[3] } @old_array;

      I have updated to post with more detail of what I am trying to do, and some code so far.
Re: Make array from field of another array
by Anonymous Monk on Apr 07, 2011 at 15:12 UTC
    What is the best way to make an array from a field within another array. I can split & iterate though, and get it working, but you folks often have a slicker way. your help is appreciated.

    That is the best way, there is nothing slicker