Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Why are elements of my array getting deleted?

by iKnowNothing (Scribe)
on Apr 18, 2012 at 22:03 UTC ( #965811=perlquestion: print w/ replies, xml ) Need Help??
iKnowNothing has asked for the wisdom of the Perl Monks concerning the following question:

Hello, I am seeing some strange behavior (strange to me at least). An array (@plist) in my perl script seems to be getting stomped on after I execute a function (ccmexec_nodie), which has no relation to said array. (no reference is ever passed to that function). However, after every call to this function, an additional element of my array gets overwritten by "undef".

Here is an example from a debugger session:

227: my $cvidPredessor = ccmexec_nodie("ccm + query -type project -name $projectName -version $predessorVersion -f + %cvid -u"); DB<3> x @plist 0 undef 1 'PROTOSIM_PrototypeSpacecraftSimulation 13755 1' DB<4> n main::UpdateSubProjects(subProjectSetup.pl:228): 228: chomp($cvidPredessor); DB<4> x @plist 0 undef 1 undef DB<5>

Note that @plist changes after executing the call to ccmexec_nodie on line 227, although it doesn't appear anywhere on that line. Infact, all I am passing to ccmexec_nodie is a string. I started seeing this behavior after using IPC::Open3 in ccmexec_nodie. I don't have any experience using this before, the reason I am using it now rather than backticks is to capture both the stdout and stderr of the system command I am executing. Thoughts?

Here is the contents of ccmexec_nodie:

sub ccmexec_nodie { my $command = $_[0]; myprint(2,"\t\t\tccmexec: $command\n"); my ($mystdin,$mystdout,$mystderr); my $pid = open3($mystdin,$mystdout,$mystderr,$command); my $myresult = ""; while(<$mystdout>){ $myresult = "$myresult$_"; } return $myresult; } sub myprint{ my $level = $_[0]; my $string = $_[1]; if($level <= 1){ print $string; } }

Comment on Why are elements of my array getting deleted?
Select or Download Code
Re: Why are elements of my array getting deleted?
by tangent (Deacon) on Apr 18, 2012 at 22:18 UTC
    Not sure if this will work but I would suggest you change how you assign your arguments to:
    sub ccmexec_nodie { my $command = shift; ... sub myprint { my $level = shift; my $string = shift;
      Not sure if this will work...

      What led you to believe it might? (I think you might say "Because I understand that accessing elements of @_ directly exhibits aliasing behavior." Fortunately, assignment of those elements performs a copy of the value of each element, so the only difference between direct assignment of indexed element and shift is that the latter updates the container—@_— as well.)

        What led you to believe it might?
        Because, in a similar scenario, this change fixed a mysterious problem I had. I don't have the knowledge to explain why, but it did work.
Re: Why are elements of my array getting deleted?
by snape (Pilgrim) on Apr 18, 2012 at 22:40 UTC

    This should work

    sub ccmexec_nodie { my $command = shift; myprint(2,"\t\t\tccmexec: $command\n"); my ($mystdin,$mystdout,$mystderr); my $pid = open3($mystdin,$mystdout,$mystderr,$command); my $myresult = ""; while(<$mystdout>){ $myresult = "$myresult$_"; } return $myresult; } sub myprint{ my ($level, $string) = @_; if($level <= 1){ print $string; } }
      Thanks for the replies. Tried both suggestions, neither one changes the behavior. I did try commenting out the call to open3, and that does in fact remove the problem. Not sure what open3 is doing to change this. FYI I am using perl 5.8.9.
Re: Why are elements of my array getting deleted?
by GrandFather (Cardinal) on Apr 18, 2012 at 23:41 UTC

    If you are using strictures (use strict; use warnings;) and ensure you aren't using global variables (make sure @plist is inside a sub for a start) then you'll probably find the problem pretty quickly.

    If that doesn't sort the issue out for you, you should pare down your code to a self contained runnable sample that shows the issue and post that. You may find I know what I mean. Why don't you? helps.

    True laziness is hard work
      I was able to recreate the behavior in this script below. Note that an element of plist is getting cleared in each iteration.
      use strict; use IPC::Open3; my @plist = ("ABC","DEF","GHI"); print "plist Before ccmexec:\n============\n",join("\n",@plist),"\n=== +=========\n\n"; my $ccmexecResult; foreach( @plist){ $ccmexecResult = ccmexec_nodie("echo HelloWorld"); print "ccmexec returned: $ccmexecResult\n"; print "plist After ccmexec:\n============\n",join("\n",@plist),"\n +============\n\n"; } sub ccmexec_nodie { my $command = $_[0]; my ($mystdin,$mystdout,$mystderr); my $pid = open3($mystdin,$mystdout,$mystderr,$command); my $myresult = ""; while(<$mystdout>){ $myresult = "$myresult$_"; } return $myresult; }
      This produced the output:
      plist Before ccmexec: ============ ABC DEF GHI ============ ccmexec returned: HelloWorld plist After ccmexec: ============ DEF GHI ============ ccmexec returned: HelloWorld plist After ccmexec: ============ GHI ============ ccmexec returned: HelloWorld plist After ccmexec: ============ ============

        Well, they do say you have to work hard to be lazy. Very often using the default variable is too lazy, as in this case. Consider:

        use strict; run(); sub run { my @plist = ("ABC", "DEF"); dumpList("Initial list", @plist); foreach (@plist) { my $ccmexecResult = ccmexec_nodie("echo HelloWorld"); dumpList("ccmexec returned: $ccmexecResult", @plist); } } sub dumpList { my ($when, @list) = @_; print "$when\n", join "\n", @list, '', ''; } sub ccmexec_nodie { my $command = $_[0]; $_ = "Well that sucks"; return "$command: result"; }

        Prints:

        Initial list ABC DEF ccmexec returned: echo HelloWorld: result Well that sucks DEF ccmexec returned: echo HelloWorld: result Well that sucks Well that sucks

        The loop variable used by for is aliased to each element in the array. If you are lazy and use the default variable for the loop variable then change the contents of the default variable you end up changing the contents of the array element being processed. The simple fix is to use an explicit loop variable:

        foreach my $element (@plist) {
        True laziness is hard work
Re: Why are elements of my array getting deleted?
by Argel (Prior) on Apr 19, 2012 at 00:08 UTC
    This is probably a long shot, look at the code below. In it, the $_ variables are apparently tripping over each other.
    #!/usr/local/perl510/bin/perl use strict; use warnings; use Data::Dumper; use IPC::Open3; my @plist = ( '/usr/bin/cat /etc/motd' ); sub ccmexec_nodie { my $command = $_[0]; my ($mystdin,$mystdout,$mystderr); my $pid = open3($mystdin,$mystdout,$mystderr,$command); my $myresult = ""; while(<$mystdout>){ $myresult = "$myresult$_"; } return $myresult; } print 'Before: ', Dumper \@plist; foreach ( @plist ) { print Dumper ccmexec_nodie $_; } print 'After: ', Dumper \@plist;
    and the output it generates:
    Before: $VAR1 = [ '/usr/bin/cat /etc/motd' ]; $VAR1 = 'Sun Microsystems Inc. SunOS 5.10 Generic January 2005 '; After: $VAR1 = [ undef ];
    Now, this does NOT duplicate your problem! But, maybe you are running into something similar? Or assigning values to aliases like @_ and the values it contains (the $_[$index] variables). Or modifying @plist from within a for/foreach loop? I get the sense you are tripping over something along those lines.

    In that light, I would suggest you eliminate all use of default variables like $_, make sure you are always using a copy of a variable, etc. and see if that changes anything. Something like:

    #!/usr/local/perl510/bin/perl use strict; use warnings; use Data::Dumper; use IPC::Open3; my @plist = ( '/usr/bin/cat /etc/motd' ); sub ccmexec_nodie { my( $command )= @_; my ($mystdin,$mystdout,$mystderr); my $pid = open3($mystdin,$mystdout,$mystderr,$command); my $myresult = ""; while(my $in=<$mystdout>){ $myresult .= $in; } return $myresult; } print 'Before: ', Dumper \@plist; foreach my $cmd ( @plist ) { print Dumper ccmexec_nodie $cmd; } print 'After: ', Dumper \@plist;
    Good luck!!

    Elda Taluta; Sarks Sark; Ark Arks
    My deviantART gallery

      Thanks for the advice, your were on the right track. I was iterating over @plist in a foreach loop, as shown in the script example I posted above. There's definitely something funny going on with the $_ variable in that loop. I was able to get a workaround going, shown below, by replacing the foreach loop with a for loop. In my larger script, I simply index into @plist when necessary using $idx.
      use strict; use IPC::Open3; my @plist = ("ABC","DEF","GHI"); print "plist Before ccmexec:\n============\n",join("\n",@plist),"\n=== +=========\n\n"; my $ccmexecResult; my $idx = 0; for ($idx = 0; $idx <= $#plist;$idx++){ $ccmexecResult = ccmexec_nodie("echo HelloWorld"); print "ccmexec returned: $ccmexecResult\n"; print "plist After ccmexec:\n============\n",join("\n",@plist),"\n +============\n\n"; } sub ccmexec_nodie { my $command = $_[0]; my ($mystdin,$mystdout,$mystderr); my $pid = open3($mystdin,$mystdout,$mystderr,$command); my $myresult = ""; while(<$mystdout>){ $myresult = "$myresult$_"; } return $myresult; }

        ARGH, no, don't do that! Actually by now you've probably already seen my earlier reply. Argel was exactly right and the suggested fix of using an explicit variable in place of the default variable is a much better solution than resorting to a C style for loop.

        As an aside for and foreach are aliases in Perl. It is the syntax that distinguishes between the Perlish iterating over a list behaviour and the Cish for loop behaviour. Personally I use for in both cases because I'm lazy and can't be bothered typing the superfluous "each".

        The important lesson to learn from this is that the special variables are effectively globals and may change without notice, especially during the processing of a called subroutine. The default variable is most prone to this issue and should be avoided where possible in any non-trivial script.

        True laziness is hard work
Re: Why are elements of my array getting deleted?
by Anonymous Monk on Apr 19, 2012 at 04:03 UTC
Re: Why are elements of my array getting deleted?
by jwkrahn (Monsignor) on Apr 19, 2012 at 04:25 UTC
    my $myresult = ""; while(<$mystdout>){ $myresult = "$myresult$_"; }

    That is usually written as:

    my $myresult = ""; while(<$mystdout>){ $myresult .= $_; }

    Or as:

    my $myresult = join '', <$mystdout>;

    Or as:

    local $/; my $myresult = <$mystdout>;

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://965811]
Approved by davido
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others contemplating the Monastery: (7)
As of 2014-08-02 08:41 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    Who would be the most fun to work for?















    Results (55 votes), past polls