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

In the following script, it appears as if the array @suffix is getting wiped out when the while(<DATA>) hits the end of file -- @suffix is only legit for the first iteration.

This behaviour is identical on ActivePerl v5.6.1 and Perl 5.6.0 (linux-i386). Any advice would be appreciated, I think it must be a logic error on my part, but I just don't see it. (there is a simple directory structure to go along with the script if you want to run it, I can email a tar file...)

Jeff Welty

### start of bug.pl @maindir = ('DIR1', 'DIR2', 'DIR3') ; @suffix = ('s1', 's2', 's3') ; ###################################################################### +########### ### this works properly ###################################################################### +########### ### for each directory in @maindir, descend into for(@maindir) { $mn = $_ ; print STDERR $mn, "\n" ; chdir $mn ; opendir(THISDIR, ".") ; @allfiles = readdir(THISDIR) ; closedir(THISDIR) ; # for each of the files in this directory, check to see if it is # a valid subdirectory by testing for the existence of a file # named 'data.txt' in that subdirectory for(@allfiles) { $sd = $_ ; if ( -r $sd . "/data.txt" ) { # Found a valid subdirectory, now we want to open 3 differ +ent # files in that subdirectory, and store some data from tho +se files # into a multidimensional hash print STDERR $sd, "\n" ; foreach(@suffix) { $sf = $_ ; print STDERR "$mn $sd $sf\n" ; # Build the filename of the data file we want $fn = "$sd/data." . $sf ; open(DATA, "<$fn") || print "Failed to open $fn\n" ; # Store the data in a multidimensional hash $v{$mn}{$sd}{$sf} = <DATA> ; close(DATA) ; } } } chdir ".." ; } ###################################################################### +########### ### Same as above, but now we use a while(<DATA>) loop at the inner-mo +st level. ### Now it doesn't work, @suffix entries are set to "" after the first + iteration. ###################################################################### +########### ### for each directory in @maindir, descend into for(@maindir) { $mn = $_ ; print STDERR $mn, "\n" ; chdir $mn ; opendir(THISDIR, ".") ; @allfiles = readdir(THISDIR) ; closedir(THISDIR) ; # for each of the files in this directory, check to see if it is # a valid subdirectory by testing for the existence of a file # named 'data.txt' in that subdirectory for(@allfiles) { $sd = $_ ; if ( -r $sd . "/data.txt" ) { # Found a valid subdirectory, now we want to open 3 differ +ent # files in that subdirectory, and store some data from tho +se files # into a multidimensional hash print STDERR $sd, "\n" ; # uncomment the following to get the behavior expected #@suffix = ('s1', 's2', 's3') ; foreach(@suffix) { $sf = $_ ; print STDERR "$mn $sd $sf\n" ; # Build the filename of the data file we want $fn = "$sd/data." . $sf ; open(DATA, "<$fn") || print "Failed to open $fn\n" ; # Store the data in a multidimensional hash (this will + overwrite the hash, but that's # not the problem with this bug. while(<DATA>) { $v{$mn}{$sd}{$sf} = $_ ; } close(DATA) ; } ## after falling out of the above loop, @suffix ## variables are now ("", "", "") } } chdir ".." ; }

Replies are listed 'Best First'.
Re: Is this a logic bug or a perl bug?
by rchiav (Deacon) on Aug 17, 2001 at 21:39 UTC
    foreach(@suffix) { $sf = $_ ; print STDERR "$mn $sd $sf\n" ; # Build the filename of the data file we want $fn = "$sd/data." . $sf ; open(DATA, "<$fn") || print "Failed to open $fn\n" ; # Store the data in a multidimensional hash (this will + overwrite + the hash, but that's # not the problem with this bug. while(<DATA>) { $v{$mn}{$sd}{$sf} = $_ ; } ....
    See the foreach at the top? It's storing data in $_. Then you're using $_ in your while loop also. The second is stomping on what's in the first. Do a
    while ($foo = <DATA>) { $v{$mn}{$sd}{$sf} = $foo; }
    At least that's what grabbed my attention first.

    Rich

      Just to nitpick, the original while loop is actually equivalent to while(defined($_ = <DATA>)) ($foo, etc.) -- note the defined.
      Thanks, that works!
Re: Is this a logic bug or a perl bug?
by premchai21 (Curate) on Aug 17, 2001 at 21:42 UTC

    The while loop uses $_, but doesn't localize it. $_ is aliased to a location in @suffix. Thus, the location in @suffix gets overwritten.

    One way to solve this would be to localize $_:

    { local $_; while (<DATA>) { ... } }

    Another way would be to use a different variable on the foreach:

    foreach $suffix (@suffix) # Or $s, or maybe use @suffixes # or @suffixen to imply plural...

    Also note, just for clarity, that DATA is a special filehandle opened to the source code after the token __DATA__. It might be a good idea to change that.

    HTH...

      Thanks, that works. Good tip about the DATA filehandle, I was rushing to pull all this out of a larger script and just made that up as fast as possible.
Re: Is this a logic bug or a perl bug?
by lemming (Priest) on Aug 17, 2001 at 21:51 UTC

    Since others have pointed out what is causing your main problems, I'd like to address some other areas

  • use strict will help you write more rugged code
  • use warnings or -w is highly recommended for much the same reason and both help in debugging
  • You check on open for failure, how about on chdir and opendir?
      I whipped the code up to duplicate the problem I was having, you sharp folks found the answer -- I wasn't trying to make this example perfect in every sense, normally, I do more of the warnings and using strict and error trapping.
Re: Is this a logic bug or a perl bug?
by dga (Hermit) on Aug 17, 2001 at 21:44 UTC

    In addition to the other comment, I would avoid using a file handle called 'DATA' just so you don't confuse people. DATA is the name of the filehandle to get stuff from the __DATA__ area of your script file.