How to find the target of a Unix symlink?

Discussion in 'Perl Misc' started by kj, May 19, 2004.

  1. kj

    kj Guest

    How does one find the target(s) of a Unix symlink?

    I guess one klugey way would be to pick through the output of
    "/bin/ls -al":

    sub get_target {
    my ($link, $seen) = @_;
    $seen ||= {};
    return $link if $seen->{$link}; # circularity
    return $link unless -e $link;
    my $ls = (`/bin/ls -al $link`)[0]; # ick!
    return $link unless $ls =~ /\s${link} ->\s+(.*?)\s*$/;
    $seen->{$link} = 1;
    get_target($1, $seen);
    }

    Is there a more civilized way to do this?

    Thanks!

    kj

    --
    NOTE: In my address everything before the period is backwards.
     
    kj, May 19, 2004
    #1
    1. Advertising

  2. kj

    Todd Guest

    kj wrote:

    > How does one find the target(s) of a Unix symlink?
    >
    > I guess one klugey way would be to pick through the output of
    > "/bin/ls -al":
    >
    > sub get_target {
    > my ($link, $seen) = @_;
    > $seen ||= {};
    > return $link if $seen->{$link}; # circularity
    > return $link unless -e $link;
    > my $ls = (`/bin/ls -al $link`)[0]; # ick!
    > return $link unless $ls =~ /\s${link} ->\s+(.*?)\s*$/;
    > $seen->{$link} = 1;
    > get_target($1, $seen);
    > }
    >
    > Is there a more civilized way to do this?
    >
    > Thanks!
    >
    > kj
    >



    perldoc -f readlink

    my $target = readlink $link_name;

    Todd
     
    Todd, May 19, 2004
    #2
    1. Advertising

  3. kj

    kj Guest

    In <c8gh1g$re5$> kj <> writes:

    >How does one find the target(s) of a Unix symlink?


    >I guess one klugey way would be to pick through the output of
    >"/bin/ls -al":


    > sub get_target {
    > my ($link, $seen) = @_;
    > $seen ||= {};
    > return $link if $seen->{$link}; # circularity
    > return $link unless -e $link;
    > my $ls = (`/bin/ls -al $link`)[0]; # ick!
    > return $link unless $ls =~ /\s${link} ->\s+(.*?)\s*$/;
    > $seen->{$link} = 1;
    > get_target($1, $seen);
    > }


    Well, the above is clearly a disaster...

    >Is there a more civilized way to do this?


    I found readlink, but my gripe with it is that it doesn't follow
    links beyond the first hop. (I.e. if "first" points to "second",
    and "second" points to "third", readlink "first" returns "second"
    not "third.) stat, on the other hand, does a nice job of finding
    the ultimate target of a sequence of links, but one ends up with
    a device and an inode, not a filename for the target.

    Is there a way to get the filename (or filenames) associated with
    a dev+inode combination?

    Thanks in advance,

    kj

    --
    NOTE: In my address everything before the period is backwards.
     
    kj, May 19, 2004
    #3
  4. kj wrote:
    >
    > How does one find the target(s) of a Unix symlink?


    perldoc -f readlink


    John
    --
    use Perl;
    program
    fulfillment
     
    John W. Krahn, May 20, 2004
    #4
  5. kj <> wrote:
    > I found readlink, but my gripe with it is that it doesn't follow
    > links beyond the first hop. (I.e. if "first" points to "second",
    > and "second" points to "third", readlink "first" returns "second"
    > not "third.) stat, on the other hand, does a nice job of finding
    > the ultimate target of a sequence of links, but one ends up with
    > a device and an inode, not a filename for the target.


    > Is there a way to get the filename (or filenames) associated with
    > a dev+inode combination?


    No, not easily. The System rarely (never?) needs to do this, so there
    is no index for it.

    You would have to 'find' on the filesystem (ususally expensive), trying
    to match the inode, and must realize that there may be more than one
    filename for it.

    --
    Darren Dunham
    Senior Technical Consultant TAOS http://www.taos.com/
    Got some Dr Pepper? San Francisco, CA bay area
    < This line left intentionally blank to confuse you. >
     
    Darren Dunham, May 20, 2004
    #5
  6. kj

    Ben Morrow Guest

    Quoth kj <>:
    > In <c8gh1g$re5$> kj <> writes:
    >
    > >How does one find the target(s) of a Unix symlink?

    >
    > I found readlink, but my gripe with it is that it doesn't follow
    > links beyond the first hop. (I.e. if "first" points to "second",
    > and "second" points to "third", readlink "first" returns "second"
    > not "third.)


    This is the only way to get all the information. A simple way to follow
    all links:

    sub readalllinks {
    my $file = shift;
    while (-l $file) {
    $file = readlink $file;
    }
    }

    > stat, on the other hand, does a nice job of finding
    > the ultimate target of a sequence of links, but one ends up with
    > a device and an inode, not a filename for the target.
    >
    > Is there a way to get the filename (or filenames) associated with
    > a dev+inode combination?


    No, except in special cases.

    Ben

    --
    Like all men in Babylon I have been a proconsul; like all, a slave ... During
    one lunar year, I have been declared invisible; I shrieked and was not heard,
    I stole my bread and was not decapitated.
    ~ ~ Jorge Luis Borges, 'The Babylon Lottery'
     
    Ben Morrow, May 20, 2004
    #6
  7. kj

    kj Guest

    In <c8gus3$hf8$> Ben Morrow <> writes:

    >Quoth kj <>:
    >> In <c8gh1g$re5$> kj <> writes:
    >>
    >> >How does one find the target(s) of a Unix symlink?

    >>
    >> I found readlink, but my gripe with it is that it doesn't follow
    >> links beyond the first hop. (I.e. if "first" points to "second",
    >> and "second" points to "third", readlink "first" returns "second"
    >> not "third.)


    >This is the only way to get all the information. A simple way to follow
    >all links:


    >sub readalllinks {
    > my $file = shift;
    > while (-l $file) {
    > $file = readlink $file;
    > }
    >}
    >


    Hmm. I think this fails if $file's target is outside $file's
    directory and is specified using a relative path. I think this
    does "the right thing":

    use Cwd;
    use File::Basename;
    sub readalllinks {
    my $cwd = my $dir = cwd . '/';
    my $file = shift;
    my $path = ($file =~ m,^/,) ? $file : "${dir}${file}";
    $dir = (fileparse($path))[1];

    while (-l $path) {
    $file = readlink $path;
    $path = ($file =~ m,^/,) ? $file : "${dir}${file}";
    $dir = (fileparse($path))[1];
    }
    (my $basename, $dir) = fileparse $path;
    chdir $dir;
    $path = cwd . "/$basename";
    chdir $cwd;
    $path;
    }

    ....but it is eggly.

    kj
    --
    NOTE: In my address everything before the period is backwards.
     
    kj, May 20, 2004
    #7
  8. kj

    Guest

    Darren Dunham <> wrote:
    > You would have to 'find' on the filesystem (ususally expensive), trying
    > to match the inode, and must realize that there may be more than one
    > filename for it.


    Or zero matches (the file might have been unlinked but still in use).

    Chris
     
    , May 20, 2004
    #8
  9. kj

    Peter Scott Guest

    Peter Scott, May 20, 2004
    #9
  10. kj

    Ben Morrow Guest

    Quoth kj <>:
    > In <c8gus3$hf8$> Ben Morrow <> writes:
    >
    > >Quoth kj <>:
    > >> In <c8gh1g$re5$> kj <> writes:
    > >>
    > >> >How does one find the target(s) of a Unix symlink?
    > >>
    > >> I found readlink, but my gripe with it is that it doesn't follow
    > >> links beyond the first hop. (I.e. if "first" points to "second",
    > >> and "second" points to "third", readlink "first" returns "second"
    > >> not "third.)

    >
    > >This is the only way to get all the information. A simple way to follow
    > >all links:

    >
    > >sub readalllinks {
    > > my $file = shift;
    > > while (-l $file) {
    > > $file = readlink $file;
    > > }
    > >}
    > >

    >
    > Hmm. I think this fails if $file's target is outside $file's
    > directory and is specified using a relative path.


    Yes, of course it does. Whoops :)

    > I think this
    > does "the right thing":
    >
    > ...but it is eggly.


    Having seen your use of Cwd, I remembered that this will do just fine:

    use Cwd qw/realpath/;

    my $file = realpath($file);

    :)

    But a cleaner rewrite would be

    use Cwd qw/cwd/;
    use File::Spec::Functions qw/rel2abs catpath splitpath/;

    sub readalllinks {
    my $rel = shift;
    my $dir = cwd;
    my $abs;

    while ( -l ($abs = rel2abs $rel, $dir) ){
    $dir = catpath +(splitpath $abs)[0,1];
    $rel = readlink $abs;
    }

    return $abs;
    }

    --
    perl -e'print map {/.(.)/s} sort unpack "a2"x26, pack "N"x13,
    qw/1632265075 1651865445 1685354798 1696626283 1752131169 1769237618
    1801808488 1830841936 1886550130 1914728293 1936225377 1969451372
    2047502190/' #
     
    Ben Morrow, May 20, 2004
    #10
  11. kj

    Joe Smith Guest

    Joe Smith, May 26, 2004
    #11
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. onetitfemme
    Replies:
    6
    Views:
    497
    onetitfemme
    Nov 2, 2005
  2. marco

    symlink overwrite

    marco, Jul 28, 2004, in forum: Python
    Replies:
    1
    Views:
    559
    Jeff Epler
    Jul 29, 2004
  3. Replies:
    4
    Views:
    769
    Daniel Dittmar
    Dec 16, 2004
  4. A. Murat Eren

    zipfile + symlink..

    A. Murat Eren, Jun 23, 2005, in forum: Python
    Replies:
    0
    Views:
    367
    A. Murat Eren
    Jun 23, 2005
  5. Marc Heiler
    Replies:
    4
    Views:
    285
    Robert Klemme
    Jun 10, 2009
Loading...

Share This Page