Danial Haris
Danial Haris

Reputation: 109

Find symlink using Find in Perl

I have faced some issue on how to find a symlink inside a directory using Find library, it can't be found and I am very new in using Perl.

use strict;
use warnings;
use File::Find::Rule;
use File::Find;
use File::Copy;

my @files = File::Find::Rule->file()
                            ->name( 'table.txt' )
                            ->in( '/a/b/c' );
for my $file (@files)   
{

    print "\nfile: $file\n";

Supposedly there is another directory that contain the specified file, table.txt, but it is a symlink that is /a/b/d, but the symlink is not there when it is printed out.

Upvotes: 1

Views: 1050

Answers (1)

zdim
zdim

Reputation: 66883

The File::Find::Rule implements the -X filetests, as methods. The one that tests whether an entry is a symlink (-l) is called symlink.

In my reading of the question you don't know the name of that directory (otherwise, why "find" a file in it?), except that it is a symbolic link. Then you need to first find directories which are symlinks, then find files in them. For this second task you'll need to tell the module to follow the links.

I use a test structure on disk

test_find/a/c/c.txt
test_find/a/b/b.txt
test_find/is_link  -> a/c  ("is_link" is a symbolic link to "a/c")

and from the directory right above it I run the program

use warnings;
use strict;
use feature 'say';

use File::Find::Rule;

my $top_dir = shift @ARGV || './test_find';

my @symlink_dirs = File::Find::Rule->directory->symlink->in($top_dir);

foreach my $dir (@symlink_dirs) {
    my @files = File::Find::Rule->file->extras({follow => 1})->in($dir);
    say "in $dir: @files";
}

which prints

in test_find/is_link: test_find/is_link/c.txt

Note that the program will find all files in all directories which are symlinks. Writing an explicit loop as above allows you to add code to decide which of the symlink-directories to actually search. If you don't mind looking through all of them you can just do

my @files = File::Find::Rule->file->extras({follow => 1})->in(@symlink_dirs);

See documentation for features to limit the search using what you know about the directory/file.


If the link directory is in a hierarchy not including the target, then simply search that hierarchy.

With test structure

other_hier/is_link  -> ../test_find/a/c
test_find/a/c/c.txt

you only need to tell it to follow the links

my @all_files = File::Find::Rule  # may use linebreaks and spaces
    -> file
    -> extras({ follow => 1 })
    -> in('other_hier');

When added to the above program and printed this adds

another/is_link/c.txt

You can of course replace the literal 'other_hier' with $top_dir and invoke the program with argument other_hier (and make that other_hier directory and the link in it).

If both link and target are in the same hierarchy that is searched then you can't do this; the search would run into circular links (the module detects that and exits with error).

Upvotes: 2

Related Questions