tags:

views:

133

answers:

3

Are both of the examples below OK, or is the second one bad style?

Case 1: Stay in top directory and use catdir to access subdirectories

#!/usr/bin/env perl
use warnings; use strict;

my $dir = 'my_dir_with_subdir';
my ( $count, $dh );

use File::Spec::Functions;
$count = 0;

opendir $dh, $dir or die $!;
while ( defined( my $file = readdir $dh ) ) {
    next if $file =~ /^\.{1,2}$/;
    my $sub_dir = catdir $dir, $file;
    if ( -d $sub_dir ) {
        opendir my $dh, $sub_dir or die $!;
        while ( defined( my $file = readdir $dh ) ) {
            next if $file =~ /^\.{1,2}$/;
            $count++;
        }
        closedir $dh or die $!;
    }
    else {
        $count++;
    }
}

closedir $dh or die $!;
print "$count\n";

Case 2: Change to subdirectories and restore top directory before exit

use Cwd;
my $old = cwd;
$count = 0;
opendir $dh, $dir or die $!;
chdir $dir or die $!;
while ( defined( my $file = readdir $dh ) ) {
    next if $file =~ /^\.{1,2}$/;
    if ( -d $file ) {
        opendir my $dh, $file or die $!;
        chdir $file or die $!;
        while ( defined( my $file = readdir $dh ) ) {
            next if $file =~ /^\.{1,2}$/;
            $count++;
        }
        closedir $dh or die $!;
        chdir $dir;
    }
    else {
        $count++;
    }
}
closedir $dh or die $!;
chdir $old or die $!;
print "$count\n";
A: 

Use File::Find, as you already proposed :)

It's almost always better to use a module for solved problems like this than to roll your own, unless you really want to learn about walking dirs...

Powertieke
I'd use Path::Class over File::Find. It has a nicer API.
David Dorward
Depends on where you're coming from. If you are used to the unix find command, File::Find is a little more familiar.
Powertieke
@David Dorward: `Path::Class` and `File::Find` don't do the same thing.
Sinan Ünür
@Shnan: `dir($input)->recurse(callback => sub …);`
David Dorward
@David Dorward: I stand corrected. Note, however, **`s/Shnan/Sinan/`**
Sinan Ünür
Whoops. Sorry, too late to edit it now too.
David Dorward
+2  A: 

Your question is whether you should change to the directories you are going through or stay in the top level directory.

The answer is: It depends.

For example, consider File::Find. The default behavior is to indeed change directories. However, the module also provides a no_chdir option in case that is not desirable.

In the case of your examples, File::Find is probably not appropriate because you do not want to recurse through all subdirectories but only one. Here is a File::Slurp::read_dir based variation on your script.

#!/usr/bin/perl

use strict; use warnings;

use File::Slurp;
use File::Spec::Functions qw( catfile );

my ($dir) = @ARGV;

my $contents = read_dir $dir;
my $count = 0;

for my $entry ( @$contents ) {
    my $path = catfile $dir, $entry;
    -f $path and ++ $count and next;
    -d _ and $count += () = read_dir $path;
}

print "$count\n";
Sinan Ünür
+1  A: 

For your example, it's best to change to subdirectories, and don't bother changing back to the original directory at the end. That's because each process has its own "current directory", so the fact that your perl script is changing it's own current directory does not mean that the shell's current directory is changed; that stays unaltered.

If this was part of a larger script it would be different; my general preference then would be not to change directory, just to reduce confusion over what the current directory is at any point in the script.

Dave Hinton