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

I'm obviously doing something wrong here but I don't know what it is. Seemed perfectly logical to me!
Anyway .. as part of a program I am currently writing I want to be able to access all the files in a couple of directories, and then send these files, one at a time, with another file read from the command line to a subroutine to do other stuff. This is the relevant bit of code.
$dir1 = $ARGV[0]; $dir2 = $ARGV[1]; $testfile = $ARGV[2]; opendir DIR1, $dir1 or die "Can't open directory $dir1: $!\n"; opendir DIR2, $dir2 or die "Can't open directory $dir2: $!\n"; @onedir = readdir(DIR1); @twodir = readdir(DIR2); closedir DIR1; closedir DIR2; for($i = 0; $i < @onedir; $i++) { next if $onedir[$i]=~/^\./; $onefile = "$dir1$onedir[$i]"; $twofile = "$dir2$twodir[$i]"; Calc($onefile, $twofile, $testfile); }
It doesn't work ... it skips though the . and .. files ok (I think), then passes the first set of files from the directories to the subroutine but then stops. so, out of the 6000 or so sets of files in those directories, I'm only producing results for the first lot.
I'm sure the code is rather clumsy but it made sense to me. I would appreciate any suggestions as to what I might be doing wrong. Also, as I have 6000 files to go though, is there a more efficent method of doing this? Thanks

Replies are listed 'Best First'.
Re: passing number of files in directory to subroutine
by GrandFather (Saint) on Sep 23, 2005 at 10:21 UTC

    The bug was most likely the missing \ between the $dir and the $filenme. The following fixes that and is a little more Perlish than Cish:

    use strict; use warnings; my ($dir1, $dir2, $testfile) = @ARGV; opendir DIR1, $dir1 or die "Can't open directory $dir1: $!\n"; opendir DIR2, $dir2 or die "Can't open directory $dir2: $!\n"; my @onedir = readdir(DIR1); my @twodir = readdir(DIR2); closedir DIR1; closedir DIR2; for my $onefile (@onedir) { my $twofile = shift @twodir; next if $onefile =~ /^\./; $onefile = "$dir1\\$onefile"; $twofile = "$dir2\\$twofile"; Calc($onefile, $twofile, $testfile); } sub Calc { my ($onefile, $twofile, $testfile) = @_; print "$onefile, $twofile, $testfile\n"; }

    Perl is Huffman encoded by design.
Re: passing number of files in directory to subroutine
by rev_1318 (Chaplain) on Sep 23, 2005 at 10:47 UTC
    Besides making your code more Perlish (as GrandFather said, but I'd use / instead of \\), I think the problem is in your Calc subroutine...

    Anyway, the code doesn't make much sense: What order do you expect the file to be in @onedir and @twodir? You'll have to do something with them to get the right order! Maybe something as:

    my @onedir = sort grep !/^\./ readdir DIR1

    Paul

      I agree wrt / versus \\ - that's my C/Windows background shining through again :).

      Pretty sure the original problem was the missing / however.

      I also think you are correct raising the issue of the file orders in the two arrays. Most OS's don't guarantee the order that files are stored in a dir and you can be pretty sure it won't be alphabetical for example. OP may be wise to sort the arrays before he uses them if the pairing order is important.


      Perl is Huffman encoded by design.
        Hi. Your code worked a treat, thanks Grandfather. As regards to the ordering of the arrays .. advice duly noted. In this case, the files are called something along these lines:
        nameoffile012298.txt
        etc .. so for one file in one directory there is a corresponding file with the exact same name and number in the other directory. Seems to be working without the need for sorting right now but I shall be watchful :)
      OK, but I thought that if it the data was in a array the order was the same as whwn you added the values in. (unlike hashes which I know are 'unordered'). There is nothing wrong with the Calc routine by the way .. that has been throughly tested.
        I thought that if it the data was in a array the order was the same as whwn you added the values in

        Yes, but the order in which the files are added to your array are not by default in alphabetic order. Most of the time, you get then in a different order, e.g. based on inode.

        Paul

Re: passing number of files in directory to subroutine
by graff (Chancellor) on Sep 24, 2005 at 00:15 UTC
    As mentioned above, it is entirely possible (and ultimately likely) that, even when two directories contain the same inventory of file names, readdir() on one will return them in a different order relative to readdir() on the other.

    So, to keep things simple, save the file names as hash keys instead of array elements; then in the for loop that calls Calc(), step through the keys of one hash, skip the call if that key is missing from the other hash, otherwise call Calc on the two.

    my %onedir = map { $_ => undef } grep /^\w/, readdir(DIR1); my %twodir = map { $_ => undef } grep /^\w/, readdir(DIR2); for my $file ( sort keys %onedir ) { next unless ( exists( %twodir{$file} )); Calc( "$dir1/$file", "$dir2/$file", $testfile ); }