Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   Perl Misc (http://www.velocityreviews.com/forums/f67-perl-misc.html)
-   -   How to find the target of a Unix symlink? (http://www.velocityreviews.com/forums/t886509-how-to-find-the-target-of-a-unix-symlink.html)

kj 05-19-2004 08:45 PM

How to find the target of a Unix symlink?
 



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.

Todd 05-19-2004 10:43 PM

Re: How to find the target of a Unix symlink?
 
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

kj 05-19-2004 10:59 PM

Re: How to find the target of a Unix symlink?
 
In <c8gh1g$re5$1@reader2.panix.com> kj <socyl@987jk.com> 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.

John W. Krahn 05-19-2004 11:44 PM

Re: How to find the target of a Unix symlink?
 
kj wrote:
>
> How does one find the target(s) of a Unix symlink?


perldoc -f readlink


John
--
use Perl;
program
fulfillment

Darren Dunham 05-19-2004 11:53 PM

Re: How to find the target of a Unix symlink?
 
kj <socyl@987jk.com> 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 ddunham@taos.com
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. >

Ben Morrow 05-20-2004 12:41 AM

Re: How to find the target of a Unix symlink?
 

Quoth kj <socyl@987jk.com>:
> In <c8gh1g$re5$1@reader2.panix.com> kj <socyl@987jk.com> 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.
~ ben@morrow.me.uk ~ Jorge Luis Borges, 'The Babylon Lottery'

kj 05-20-2004 03:47 AM

Re: How to find the target of a Unix symlink?
 
In <c8gus3$hf8$3@wisteria.csv.warwick.ac.uk> Ben Morrow <usenet@morrow.me.uk> writes:

>Quoth kj <socyl@987jk.com>:
>> In <c8gh1g$re5$1@reader2.panix.com> kj <socyl@987jk.com> 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.

chris-usenet@roaima.co.uk 05-20-2004 09:18 AM

Re: How to find the target of a Unix symlink?
 
Darren Dunham <ddunham@redwood.taos.com> 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

Peter Scott 05-20-2004 04:42 PM

Re: How to find the target of a Unix symlink?
 
In article <c8gh1g$re5$1@reader2.panix.com>,
kj <socyl@987jk.com> writes:
>
>How does one find the target(s) of a Unix symlink?

[...]
>Is there a more civilized way to do this?


File::Spec::Link

--
Peter Scott
http://www.perldebugged.com/
*** NEW *** http://www.perlmedic.com/

Ben Morrow 05-20-2004 05:32 PM

Re: How to find the target of a Unix symlink?
 

Quoth kj <socyl@987jk.com>:
> In <c8gus3$hf8$3@wisteria.csv.warwick.ac.uk> Ben Morrow <usenet@morrow.me.uk> writes:
>
> >Quoth kj <socyl@987jk.com>:
> >> In <c8gh1g$re5$1@reader2.panix.com> kj <socyl@987jk.com> 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.me.uk


All times are GMT. The time now is 07:22 PM.

Powered by vBulletin®. Copyright ©2000 - 2013, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57