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

There is a very weird thing happening when I’m using the last; statement. I'm comparing two arrays to find if any elements are missing off one. The strange thing that is happening is when I go to break out of a while loop with the last; statement the loop claims 2 elements are missing. When I use the next statement the loop can find every element. I know for a fact every element is there. The arrays are the same in both test cases. Here is a snippet of code. Can anyone explain to me why this is happening and how to fix it.
#Here are a few elements of the hash %prolist = ( '1', 'CMD', '2', '/bin/sh', '3', 'rds', '4', 'SysExec', '5', '/oasis/bin/sysmenu', '6', '/oasis/bin/TS_TextSrvcs', } foreach $val(sort keys(%prolist)){ while(<@list>){ if ("$prolist{$val}" eq "$_" ){ $set = 1; last; } } if ($set == 0){print "NF: $prolist{$val}\n"; } $set = 0; }
Its always the same 2 elements too. #2 and #3

Replies are listed 'Best First'.
Re: loop control
by tachyon (Chancellor) on Feb 04, 2003 at 00:24 UTC

    If you are going to store one list as a hash why not use it as a lookup table like so:

    %prolist = ( '1' => 'CMD', '2' => '/bin/sh', '3' => 'rds', '4' => 'SysExec', '5' => '/oasis/bin/sysmenu', '6' => '/oasis/bin/TS_TextSrvcs', ); my @list = 1 .. 7; my $missing = 0; for my $item (@list) { unless (exists $prolist{$item}) { print "Missing item $item!\n"; $missing = $item; last; } } print $missing ? "$missing was missing!" : "All present and accounted +for";

    cheers

    tachyon

    s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

Re: loop control
by VSarkiss (Monsignor) on Feb 04, 2003 at 03:13 UTC

    This is a very odd construct: while (<@list>){Are you sure it's what you intended? What do you think it's going to do? What's in @list when you run this code?

    Also, this line makes me think you're converting a shell script or an awk program: if ("$prolist{$val}" eq "$_" ){since quoting those variables is unnecessary.

    With those warning signs, I don't want to hazard a guess on what might be wrong. Could you provide some more info on what you're trying to do and what doesn't go the way you intended?

      I think the angle brackets around the @list triggers the globbing construct.
Re: loop control
by dragonchild (Archbishop) on Feb 03, 2003 at 23:26 UTC
    Your description of the problem isn't clear. This makes me think that you're not very clear on the problem you're trying to solve with this code.
    • <nit>Line up your closing braces better.</nit>
    • Where is @list coming from?
    • Can you describe in English what it is you're trying to do?
    What I see your code doing is this:
    FOREACH value IN list1 WHILE value2 IN list2 IF value1 EQ value2 ASSIGN set = 1 BREAK END IF END WHILE IF set EQ 0 PRINT message END IF ASSIGN set = 0 END FOREACH
    Are you sure you want it eq and not ne?

    ------
    We are the carpenters and bricklayers of the Information Age.

    Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

Re: loop control
by steves (Curate) on Feb 03, 2003 at 23:18 UTC

    Can you show us the loop using next that works and show us the data assigned to @list?

Re: loop control
by mce (Curate) on Feb 04, 2003 at 14:01 UTC
    Hi,

    Looking at your code, I think you want to enter the if statement, if you have a file in your directory that has the same name as a file in the %prolist. Is that why you are using the glob for?

    Why not using the -f test operator??

    I fail to see what the while loop does in your case, so please clarify


    ---------------------------
    Dr. Mark Ceulemans
    Senior Consultant
    IT Masters, Belgium

      Sorry I wasn't so clear. Let me go into a little more detail on what I’m trying to accomplish. The @list is coming from a file handler that returns a list from a sys. command. The length varies is so I’m just cycling through it in while statement. In a nut shell all I’m doing is looking to see if all the elements are present in the new "@list" and let the user know which elements are missing.
      open($syscmd,"ps -a |") || die "unable to open system command $!\n"; $count =0; while (<$syscmd>){ @list1 = split(" ", $_); @list[$count] = @list1[3]; $count = $count + 1; } $set = 0; foreach $val(sort keys(%prolist)){ #checks through every element +in the hash one by one. while(<@list>){ #looks through each element in list for a matc +h if ($prolist{$val} eq $_ ){ #if the match is found $set = 1; #sets this to true so that the below if sta +tment isn't executed last; } } if ($set == 0){ #prints only if there is no match print + "NF: $prolist{$val}\n"; } $set = 0; #resets boolen }
        you read all the lines from a file handle ($syscmd) and take the third column of each and put this in @list. You then want to see which elements of a second list are NOT in @list. Here's how i'd do it: just remove from %pro all of the elements in @list.
        # the values here don't matter, all we really # care about is the existence of the keys my %pro = map { $_, 1 } qw[CMD /bin/sh rds SysExec /oasis/bin/sysmenu /oasis/bin/TS_TextSrvcs]; # get the third column from every line of $syscmd my @list = map { (split " ")[3] } <$syscmd>; # remove the keys of %pro in @list delete @pro{@list}; # print the error message foreach my $not_found (keys %pro) { print "Didn't find $not_found in output of system cmd.\n"; }

        OK, that's a lot clearer. The basic thing you're doing wrong is using angle brackets to go through a list, which is not necessary. The angle brackets are generally associated with doing I/O.

        But you're also doing a lot of unnecessary work, like the way you're counting up the number of items you're pushing into your list. Perl has constructs for doing that kind of stuff much more easily; you can even do it in one line, like this: push @list, (split ' ')[3] foreach <$syscmd>;In case that's too compact, here's the equivalent broken up a little more:

        while (<$syscmd>) { my @temp = split ' '; push @list, $temp[3]; }
        Of course, this also takes up more space, for the temporary array.

        Your main loop searches an array for a matching element. That's a common enough construct that perl has a special construct for it, the grep function. Here's how you'd use it in this context:

        foreach my $val (sort keys %prolist) { unless (grep($prolist{$val} eq $_)) { print "NF: $prolist{$val}\n"; } }
        There are ways to tighten up that loop, but this lets you see what's going on. Your original code can be fixed by just taking out the angle brackets in the while, but you may as well pick up some perl nifty tricks while you're at it.

        (BTW, I haven't tested any of this code.)

Re: loop control
by Aristotle (Chancellor) on Feb 05, 2003 at 00:14 UTC
    The way you assign a number as a key for each element of %prolist seems pointless, if I read your intentions correctly. Here's what you're probably trying to do:
    #!/usr/bin/perl -w use strict; my @required = qw( CMD /bin/sh rds SysExec /oasis/bin/sysmenu /oasis/bin/TS_TextSrvcs ); # invoke ps -a using backticks, read result into a list # for each line, split on whitespace, keep 3rd element # return a list of element => undef pairs my %procs = map { (split ' ')[3], undef } qx/ps -a/; # %procs now has a key named after each process # values are empty, mere existence of the keys suffices # now for each required process, # check if it has an entry in the hash of running procs my @missing = grep !exists $procs{$_}, @required; # print the resulting list of missing processes print map "NF: $_\n", @missing;
    Or more condensed:
    #!/usr/bin/perl -w use strict; my %procs = map { (split ' ')[3], undef } qx/ps -a/; print map "NF: $_\n", grep !exists $procs{$_}, qw( CMD /bin/sh rds SysExec /oasis/bin/sysmenu /oasis/bin/TS_TextSrvcs );
    Note however that most every variant of ps has some switch to make it output only process names, in which case the split becomes superfluous. Assuming GNU ps:
    my %procs = map { chomp; $_ => undef } qx/ps -a ho comm/;

    This is much more robust, as it avoids the need to parse an external program's output.

    All that said and done, if you want to monitor your system, you should not reinvent the wheel. Nagios, mon and Big Brother will do many things for you. Don't waste your time.

    Makeshifts last the longest.

      Thanks a lot guys i'v implimented a few ideas and things are running very smoothly now.