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!!
| [reply] [d/l] [select] |
|
|
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;
}
| [reply] [d/l] |
|
|
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
| [reply] |
Re: Why are elements of my array getting deleted?
by GrandFather (Saint) 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
| [reply] |
|
|
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:
============
============
| [reply] [d/l] [select] |
|
|
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
| [reply] [d/l] [select] |
Re: Why are elements of my array getting deleted?
by jwkrahn (Abbot) on Apr 19, 2012 at 04:25 UTC
|
my $myresult = "";
while(<$mystdout>){
$myresult .= $_;
}
Or as:
my $myresult = join '', <$mystdout>;
Or as:
local $/;
my $myresult = <$mystdout>;
| [reply] [d/l] [select] |
Re: Why are elements of my array getting deleted?
by Anonymous Monk on Apr 19, 2012 at 04:03 UTC
|
| [reply] |
Re: Why are elements of my array getting deleted?
by tangent (Parson) 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;
| [reply] [d/l] |
|
|
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.)
| [reply] [d/l] [select] |
|
|
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.
| [reply] |
Re: Why are elements of my array getting deleted?
by snape (Pilgrim) on Apr 18, 2012 at 22:40 UTC
|
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;
}
}
| [reply] [d/l] |
|
|
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.
| [reply] |