AlexPham
AlexPham

Reputation: 321

File::Find in Perl - Looking for files only

I have a script like this to list every FILES inside my root path

use strict;
use File::Find qw(find);
my $path = "<my root path>";

find(\&Search, $path);

sub Search{
    my $filename = $File::Find::name;
    if(-f $filename){
        print $filename."\n";
    }   
}

My point is to try to list all the FILES. However, it also listed the symlink inside my $root. I modify my Search function like this and it worked:

sub Search{
    my $filename = $File::Find::name;
    #Check if $filename is not symlink first
    if(!-l $filename){
        if(-f $filename){
            print $filename."\n";
        }  
    }   
}

But it seem awkward right ? Why do we need two if condition just to verify $filename is the real file and not a symlink !!!

Is there anyone can suggest a better, more decent solution for this ?

Thank you and best regards.

Alex

Upvotes: 1

Views: 1124

Answers (2)

ikegami
ikegami

Reputation: 385655

stat and lstat are identical except when it comes to symlinks. The former collects information about the linked file, whereas the latter collects information about the link itself.

The -X EXPR uses stat. lstat is needed here.

sub Search {
    my $filename = $File::Find::name;

    if (!lstat($filename)) {
        warn("Can't stat $filename: $!\n");
        return;
    }

    say $filename if -f _;
}

Bonus: Error checking becomes much simpler when you pre-call stat or lstat.

Upvotes: 2

Sobrique
Sobrique

Reputation: 53478

-f is testing for file, and that includes symlinks. So yes, you do have to test both.

One slightly useful thing, is that you can probably just do:

if ( -f and not -l ) { 

because File::Find sets $_ to the current file, and the file tests default to using that too. (won't work if you turn on no_chdir though).

You may also want to consider File::Find::Rule as an alternative to File::Find.

Upvotes: 2

Related Questions