How to list symbolic link chains?
Solution 1
Just use namei
:
$ namei d
f: d
l d -> c
l c -> b
l b -> a
d a
Solution 2
readlink -e <link>
readlink [OPTION]... FILE
- -e, --canonicalize-existing
canonicalize by following every symlink in every component of the given name recursively, all components must exist
$ mkdir testlink
$ cd testlink
pjb@pjb-desktop:~/testlink$ ln -s c b
pjb@pjb-desktop:~/testlink$ ln -s b a
pjb@pjb-desktop:~/testlink$ ls -l
total 0
lrwxrwxrwx 1 pjb pjb 1 2010-02-23 08:48 a -> b
lrwxrwxrwx 1 pjb pjb 1 2010-02-23 08:48 b -> c
pjb@pjb-desktop:~/testlink$ echo foo > c
pjb@pjb-desktop:~/testlink$ cat a
foo
pjb@pjb-desktop:~/testlink$ readlink -e a
/home/pjb/testlink/c
note: readlink a by itself returns b
note #2: together with find -l, a utility to list the chains could easily be written in perl, but also has to be smart enough to detect loops
readlink will not output anything if you have a loop. This is better than getting stuck, I suppose.
pjb@pjb-desktop:~/testlink$ ln -sf a c
pjb@pjb-desktop:~/testlink$ ls -l
total 0
lrwxrwxrwx 1 pjb pjb 1 2010-02-23 08:48 a -> b
lrwxrwxrwx 1 pjb pjb 1 2010-02-23 08:48 b -> c
lrwxrwxrwx 1 pjb pjb 1 2010-02-23 09:03 c -> a
pjb@pjb-desktop:~/testlink$ readlink -e a
pjb@pjb-desktop:~/testlink$ # (note: no output)
Solution 3
Here is a recursive function in Bash:
chain() { export chain; local link target; if [[ -z $chain ]]; then chain="$1"; fi; link=$(stat --printf=%N $1); while [[ $link =~ \-\> ]]; do target="${link##*\`}"; target="${target%\'}"; chain+=" -> $target"; chain "$target"; return; done; echo "$chain"; unset chain; }
On multiple lines:
chain() {
export chain
local link target
if [[ -z $chain ]]
then
chain="$1"
fi
link=$(stat --printf=%N "$1")
while [[ $link =~ \-\> ]]
do
target="${link##*\`}"
target="${target%\'}"
chain+=" -> $target"
if [[ ! $target =~ / && $1 =~ / ]]
then
target="${1%/*}/$target"
fi
chain "$target"
return
done
echo "$chain"
unset chain
}
Examples:
$ chain d
d -> c -> b -> a
$ chain c
c -> b -> a
$ chain a
a
It requires stat(1)
which may not be present on some systems.
It will fail if names contain backticks, single quotes, or "->". It gets stuck in a loop with symlink loops (this could be solved using an associative array in Bash 4). It exports a variable called "chain" without regard to whether it's already in use.
There may be other problems with it.
Edit:
Fixed a problem with some relative symlinks. Some still don't work, but the version below doesn't require the target of the link to exist.
Added a version that uses readlink:
chain ()
{
export chain;
local target;
if [[ -z $chain ]]; then
chain="$1";
fi;
target=$(readlink "$1");
while [[ $target ]]; do
chain+=" -> $target";
if [[ ! $target =~ / && $1 =~ / ]]
then
target="${1%/*}/$target"
fi
chain "$target";
return;
done;
echo "$chain";
unset chain
}
Related videos on Youtube
Kalecser
Updated on September 17, 2022Comments
-
Kalecser over 1 year
Given this example:
mkdir a ln -s a b ln -s b c ln -s c d
If I execute:
ls -l d
It will show:
d -> c
Is there a way for
ls
or any other linux command to showd -> c -> b -> a
instead?-
Paul about 14 yearsI cross posted this as stackoverflow.com/questions/2320277/… I hope you don't mind.
-
-
Kalecser about 14 yearsI've tested your script and it really works but I prefer something simpler so I've accepted the other answer even if incomplete.
-
Stefan Lasiewski over 13 yearsNice script. Sometimes I want to see the entire chain, and
readlink
doesn't seem to show that. Java on Ubuntu is:/usr/bin/java -> /etc/alternatives/java -> /usr/lib/jvm/java-6-openjdk/jre/bin/java
-
Tom O'Connor over 12 yearsTIL. This is a cool command. upvotes
-
Jose Alban about 8 yearsOn OS X:
brew install coreutils
andgreadlink -e <link>
-
phobic over 7 yearsThis should be the accepted answer, as it shows the complete chain of links as requested in the op. Further it can be used to list an entire directory similar to
ls
. Thanks! -
Kalecser about 7 yearsIndeed, was revisiting this post and namei is a wonderful command!
-
pdem about 7 yearsuse with "which" command to find the actual usage of a program: $namei `which java`