In this tutorial, we’ll discuss how to list a chain of symbolic links.
2. Introduction to the Problem
As an example of a chain of symbolic links, we can check the java command:
$ which java /usr/bin/java $ ls -l /usr/bin/java lrwxrwxrwx. 1 root root 22 Apr 21 2022 /usr/bin/java -> /etc/alternatives/java $ ls -l /etc/alternatives/java lrwxrwxrwx 1 root root 64 Apr 21 2022 /etc/alternatives/java -> /usr/lib/jvm/java-11-openjdk-126.96.36.199.8-4.el8_5.x86_64/bin/java $ ls -l /usr/lib/jvm/java-11-openjdk-188.8.131.52.8-4.el8_5.x86_64/bin/java -rwxr-xr-x 1 root root 16016 Dec 31 2021 /usr/lib/jvm/java-11-openjdk-184.108.40.206.8-4.el8_5.x86_64/bin/java
As we see, the java command is in fact a symbolic link. The final target in the chain is /usr/lib/jvm/java-11-openjdk-220.127.116.11.8-4.el8_5.x86_64/bin/java. However, we had to use three ls commands to find the final target. It would be better if we could list the chain of symbolic links using a single command.
3. Example Setup
Let’s create a number of symbolic links to analyze the problem:
$ pwd /tmp $ mkdir directory1 $ touch directory1/file1 $ ln -s directory1/file1 link1 $ ln -s link1 link2 $ ln -s link2 link3
First, we created a directory, directory1, in the /tmp directory using the mkdir command. Then, we created a file, file1, in /tmp/directory1 using the touch command. Finally, we created three symbolic links. The first one, link1, is a symbolic link to directory1/file1. The second one, link2, is a symbolic link to link1. The final link, link3, is a symbolic link to link2. We created the symbolic links using the ln command with its -s option.
Now, let’s look at the chain of symbolic links we’ll be working with throughout the tutorial:
link3 -> link2 -> link1 -> directory1/file1
How can we list this chain on the command line? We’ll try to find several solutions in the remaining sections.
4. The namei Command
We can use the namei command for resolving a symbolic list chain:
$ namei link3 f: link3 l link3 -> link2 l link2 -> link1 l link1 -> directory1/file1 d directory1 - file1
The f: part of the first line shows the pathname currently being resolved. It’s link3.
The l in the second line means that link3 is a symbolic link to link2. In a similar fashion, link2 is a symbolic link to link1, and link1 is a symbolic link to directory1/file1. That’s what the third and fourth lines show.
The d in the fifth line specifies that directory1 is a directory. Finally, the character – in the last line points out that file1 is a regular file.
As we can see, namei follows the given pathname until it finds an endpoint that isn’t a symbolic link.
By default, namei displays the characters indented at the beginning of each line. The characters denote the type of each file in the chain. If we don’t want the indentation, we can use the -v option. This option aligns those characters vertically. We can also use the —vertical option instead of -v. They’re the same.
Let’s check the output of namei using the -v option:
$ namei -v link3 f: link3 l link3 -> link2 l link2 -> link1 l link1 -> directory1/file1 d directory1 - file1
The output is basically the same as the previous one. However, the first character on each line is aligned vertically this time, as expected.
5. The readlink Command
The readlink command is another option for listing a chain of symbolic links.
5.1. Basic Usage of readlink
Normally, if we use readlink without any options, it just shows the first component in the chain:
$ readlink link3 link2
Since link3 is a symbolic link to link2, it just displays link2.
However, we can use its -e option to show the last component of the chain:
$ readlink -e link3 /tmp/directory1/file1
5.2. Using readlink to List a Symbolic Link Chain
Unfortunately, readlink doesn’t have an option that shows the whole chain like namei. However, we can use a script, follow_link.sh, for displaying the whole chain using readlink:
#!/bin/bash current=$1 while [[ -L $current ]] do next=$(readlink $current) dir_name=$(dirname $current) cd $dir_name echo "$current -> $next" current=$next done
First, we get the path to be resolved as input and assign the path to the shell variable, current.
Then, we iterate the path in a while loop. The condition of the while loop, [[ -L $current ]], checks whether the file is a symbolic link. As long as it’s a symbolic link, the condition of the while loop is true. In that case, we get the next component in the chain using the statement next=$(readlink $current). We store the next node in the chain in the shell variable next.
Then, we extract the directory part of the filename using the dir_name=$(dirname $current) command. We store the path in the shell variable dir_name. We change the current directory to the directory stored in dir_name using the cd $dir_name statement because the symbolic link may not point to an absolute path.
Finally, after printing the current component of the chain using the echo “$current -> $next” statement, we update the current component of the chain for the next iteration. We achieve this using the current=$next statement.
Having understood the operation of the script, let’s give it a try:
$ pwd /tmp $ follow_link.sh /tmp/link3 /tmp/link3 -> link2 link2 -> link1 link1 -> directory/file1 $ follow_link.sh link3 link3 -> link2 link2 -> link1 link1 -> directory/file1
We ran the script twice. In the first run, the input of the script was an absolute path. However, it was a relative path in the second run. The outputs of the script in both runs were as expected.
Let’s try the script also for /usr/bin/java:
$ follow_link.sh /usr/bin/java /usr/bin/java -> /etc/alternatives/java /etc/alternatives/java -> /usr/lib/jvm/java-11-openjdk-18.104.22.168.8-4.el8_5.x86_64/bin/java
In this case, all the links were absolute symbolic links. The result was again successful as expected.
In this article, we discussed how to list a chain of symbolic links.
First, we discussed the namei command, which is useful for displaying a symbolic link chain as a tree.
Then, we saw that we can show the final component of the symbolic link chain using the -e option of readlink. We also looked at a simple script that uses readlink to list the whole symbolic link chain.