1. Introduction

File linking enables access to the same file with multiple names and paths. There are two types of links:

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.

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?

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.

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.

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.

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:

  1. Convert the relative symlink to an absolute symlink
  2. Relocate the symlink as necessary
  3. 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.

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.