Finding all the links in a Unix file/directory path

Do you have a question? Post it now! No Registration Necessary.  Now with pictures!

Threaded View

I am working on a Perl script that copies files from a log  file. Some
of the files are links. The link however is in an intermediate
directory. For example, in a path /tmp/test_hier/b/f/of3.cpp, the
actual path is /tmp/test_hier/b/c/of3.cpp.

I have attached a that creates the sample directory
hierarchy and a DirGen.t that prints out the information that the file
is a link.

My Questions:
1) Does anyone know how I can recursively trace the links so that I
copy the "exact" hierarchy? For example, I want to copy *everything*
in /tmp/test_hier underneath /tmp/copy/tmp/test_hier (verbatim).
2) The complication is that there might be arbitrary number of links
between the linked path and the actual path. How can I trace all of

All help is appreciated!


# this package creates a sample directory structure
package DirGen;

sub new {
    my $class = shift;
    my $work_dir = shift;
    my $self = {};
    $self-> = $work_dir;

    # create a sample hieararchy underneath $work_dir
    my @args = ("rm -rf $work_dir && mkdir -p $work_dir");
    system(@args) ==0 or die "system command failed:$?";

    # go to the working dir
    chdir "$work_dir" or  die "system command failed:$?";

    # the test directory tree
    # $work_dir/
    #           b/ - f -> c/     # f is a link to directory c/
    #             c/ - of3.cpp
    my @dir_tree = ("mkdir -p b/c",
                    "touch b/c/of3.cpp",
                    "echo 4 > b/c/of3.cpp",
                    "cd b && ln -s c f");


    # access of3.cpp path via the directory link
    my $path = "$work_dir/b/f/of3.cpp";

    return $self;

# This function creates a predefined directory hierarchy
sub generate_test_hier_ {
    my $self = shift;
    my $dir_tree_cmds = shift;
    my @args = ();
    foreach my $path (@$dir_tree_cmds) {
      my @args = ($path);
      system(@args) ==0 or die "system command failed:$?";

sub get_iterator {
    my $self = shift;
    my $pos = -1;
    return sub {
        return undef if ($pos > scalar(@}));
        return  $}[++$pos];

----- end of

------ DirGen.t

eval '(exit $?0)' && eval 'exec perl -w -S $0 $' && eval 'exec
perl -w -S $0 $argv:q'  if 0;
# This script tests

require 5.006; # need perl version 5.6 or higher
use strict;      # comment out these two
use diagnostics; # and '-w' switch, once testing is over
use File::Basename;
use Cwd qw(realpath);

use DirGen;

my $dirTreeGen = DirGen->new("/tmp/test_hier");

my $dir_it = $dirTreeGen->get_iterator();
while (my $path = $dir_it->()) {
  my $target_dir = dirname($path);
  my $orig_dir = realpath($target_dir);# realpath works only with
  if ($orig_dir ne $target_dir) {
    print "$path is a link, the actual path is $orig_dir/".basename
-------- end of DirGen.t

Re: Finding all the links in a Unix file/directory path

Quoted text here. Click to load it

The test for a symbolic link in perl is: -l $name

Quoted text here. Click to load it

To make a directory in perl: mkdir $dirname;
To create a symbolic link in perl:  symlink OLDFILE,NEWFILE;

Re: Finding all the links in a Unix file/directory path

Looks like my question was not clear:

In my logfile, i see the following path: /tmp/test_hier/b/f/of3.cpp.
Since directory 'f' is a symbolic link to directory 'c', the actual
path is /tmp/test_hier/b/c/of3.cpp.

I want to copy the orig path  /tmp/test_hier/b/f/of3.cpp to my target
directory: /tmp/copy/ . here is what I want to do:

1) Copy the actual file /tmp/test_hier/b/c/of3.cpp as /tmp/copy/tmp/
2) Create a link:  /tmp/copy/tmp/test_hier/b/f ->  /tmp/copy/tmp/

Now /tmp/copy/tmp/test_hier/ is the same as /tmp/test_hier/. the
problems are:

1) 'realpath' just returns the actual file. It does not return how the
symbolic link is traced through the directories and the actual file is
found. I need that information.

2) there could an arbitrary number of links between the first symbolic
link file (/tmp/test_hier/b/f/of3.cpp) and the actual file i.e /tmp/
test_hier/b/f -> /tmp/test_hier/b/d -> /tmp/e -> /home/usr1/of3.cpp.

The problem: I have 2 paths in my hand: the symbolic link file ( /tmp/
test_hier/b/f/of3.cpp) and the actual file ( /home/usr1/of3.cpp).

Do any of you have any idea how to trace the intermediate symbolic

Re: Finding all the links in a Unix file/directory path

Quoted text here. Click to load it

readlink is the basic tool here. You will need to check the file and
then all enclosing directories up to the root, and resolve any links you
find yourself. Be careful to resolve relative links correctly. You will
then need to repeat the process on the resolves link.


Re: Finding all the links in a Unix file/directory path

Quoted text here. Click to load it

So, if I understand correctly you want to copy the complete _structure_
of the the file tree, i.e. if a directory entry was a symlink in the
original then it should become a symlink in the copy, too.

I _think_ this is exactly the behaviour of File::Copy::Recursive. At
least the doc says

If your system supports symlinks then symlinks will be copied as
symlinks instead of as the target file. Perl's symlink() is used instead
of File::Copy's copy() You can customize this behavior by setting
$File::Copy::Recursive::CopyLink to a true or false value.


Re: Finding all the links in a Unix file/directory path

Quoted text here. Click to load it

No, AIUI he doesn't want to copy the whole tree, just one file and all
symlinks required to resolve that. So if he starts with

    /a/b -> /c
    /c/d -> /e
    /g   -> /a/b/d/f

and copys /g to /tmp/copy he wants to end up with

            b -> /tmp/copy/c
            d -> /tmp/copy/e
        g -> /tmp/copy/a/b/d/f

In particular, he seems to want to follow and copy links that point
anywhere on the filesystem, without copying the whole disk. This is not
straightforward, but can be done with -l, readlink,
File::Basename::dirname, and a little care.


Site Timeline