1. Introduction
File linking enables access to the same file with multiple names and paths. There are two types of links:
- symbolic link (symlink), only pointing to the original file (target)
- hard link, representing another path for the inode of the target
Files disappear once all their hard links are gone. On the other hand, symlinks are mostly an accessibility and organization convenience. As such, we can even create them with relative instead of absolute (full) paths to the original target. In this case, they are called relative symlinks.
In this tutorial, we look at the standard way to move or copy a relative symbolic link (symlink) without breaking its connection to the origin. First, we define relative and absolute symlinks. Next, we convert between the two types of links. Finally, we show how to safely relocate a relative symbolic link.
We tested the code in this tutorial on Debian 11 (Bullseye) with GNU Bash 5.1.4. It should work in most POSIX-compliant environments.
2. Relative and Absolute Symbolic Links
Generally, relative paths have several disadvantages when compared to absolute paths.
Still, a relative path in a symlink is helpful:
- no hardcoded external filesystem structure
- moving the relative link and target in unison preserves their relationship
Thus, relative symlinks can be very convenient for activities like installation, deployment, and porting as long as we preserve their relation.
Naturally, we create links with the POSIX-standard ln command. So, let’s leverage ln to generate relative and absolute symlinks to a file:
$ ln --symbolic --relative /file /filesymrel
$ ln --symbolic /file /filesymabs
Now, we can check what each points to with ls:
$ ls -l /file*
-rw-r--r-- 1 baeldung baeldung 0 Jan 10 01:00 /file
lrwxrwxrwx 1 baeldung baeldung 5 Jan 10 01:01 /filesymabs -> /file
lrwxrwxrwx 1 baeldung baeldung 4 Jan 10 01:01 /filesymrel -> file
Critically, the path that /filesymrel points to doesn’t begin with a slash. Indeed, that means it’s a relative symlink.
Let’s try to read from it before and after a move:
$ cat /filesymrel
Content.
$ mv /filesymrel /dir/
$ cat /dir/filesymrel
cat: /dir/filesymrel: No such file or directory
As expected, unlike absolute links, we can’t read a symlink once its location relative to the target changes. What can we do about that?
3. Convert Between Relative and Absolute Symlink
Similar to paths, we can always switch between relative and absolute symlinks. To do this, we use two commands:
- readlink with its -f or –canonicalize flag, which extracts the absolute target path from a symlink
- ln to recreate and –force an overwrite of the –symbolic link
In fact, we can apply both commands in one go. Let’s take our earlier symbolic link /filesymrel:
$ readlink /filesymrel
file
First, we convert the relative symlink to an absolute symlink:
$ ln --force --symbolic "$(readlink --canonicalize /filesymrel)" /filesymrel
Now, we can recheck the link target:
$ readlink /filesymrel
/file
As expected, it’s now an absolute path.
Next, we convert an absolute symlink to a –relative symlink:
$ ln --force --symbolic --relative "$(readlink --canonicalize /filesymrel)" /filesymrel
At this point, we again have a relative link:
$ readlink /filesymrel
file
It’s now time to apply this concept to a move or copy.
4. Preserve Relative Symlink During Location Changes
After getting to know relative symlinks, let’s try to move them without breaking their function.
To begin with, we create a relative symlink to /file and confirm its path as well as the contents of the target:
$ ln --symbolic --relative /file /filesymrel
$ readlink /filesymrel
file
$ cat /filesymrel
Content.
Time to replicate the relocation problem.
4.1. Relative Symlink Relocation Problem
Now, we try a direct copy of the symlink to another location and verify it breaks:
$ mv /filesymrel /dir/
$ readlink /dir/filesymrel
file
$ cat /dir/filesymrel
cat: /dir/filesymrel: No such file or directory
Critically, if there was a file at /dir/file, we’d now see its contents instead of those of /file, the original target. Since this also isn’t what we want, let’s see how we can mitigate confusion.
4.2. Convert and Move Symlink
At this point, we can try our solution:
$ ln --force --symbolic "$(readlink --canonicalize /filesymrel)" /filesymrel
$ mv /filesymrel /dir/filesymrel
$ ln --force --symbolic --relative "$(readlink --canonicalize /dir/filesymrel)" /dir/filesymrel
There are three steps here:
- Convert the relative symlink to an absolute symlink
- Relocate the symlink as necessary
- Convert the relocated symlink back to relative
Next, we verify the path and target contents of the moved /dir/filesymrel:
$ readlink /dir/filesymrel
../file
$ cat /dir/filesymrel
Content.
As expected, we can now read the contents of the target via the symlink, because its path is correct.
Actually, this solution can be optimized.
4.3. Directly Create Symlink at Destination
We can create a new symlink based on the original in the destination path directly:
$ ln --symbolic --relative "$(readlink --canonicalize /filesymrel)" /dir/filesymrel
$ readlink /dir/filesymrel
../file
$ cat /dir/filesymrel
Content.
After this, we can remove the original, if desired.
5. Summary
In this article, we discussed a standard way to relocate relative symbolic links without severing the connection with their target. Basically, it comes down to a single command:
$ ln --symbolic --relative "$(readlink --canonicalize SOURCE_SYMBOLIC_LINK)" DESTINATION_SYMBOLIC_LINK
In conclusion, the standard ln Linux tool enables us to move and copy relative symbolic links without breaking them.