Why does the "ln" command need an absolute path?

5,006

Solution 1

A symbolic link stores the path you give when you create it. Paths do not work (symlinks are broken), when the file is not actually in that path. Let's make a symlink with a relative path...

zanna@toaster:~/playground$ mkdir linkyland anotherplace
zanna@toaster:~/playground$ cd linkyland
zanna@toaster:~/playground/linkyland$ ln -s sauce target
zanna@toaster:~/playground/linkyland$ file *
target: broken symbolic link to sauce

ln doesn't care whether the source file exists (so if you make a typo in the path, it won't complain). Let's create the file we want to link to and see if that helps:

zanna@toaster:~/playground/linkyland$ > sauce
zanna@toaster:~/playground/linkyland$ file target
target: symbolic link to sauce

Now the link works. We can use only the basename (the last element of the path) because sauce is in the same directory as target, so target can store the path sauce and that is enough information to find the sauce when we need it.

zanna@toaster:~/playground/linkyland$ cd ../anotherplace
zanna@toaster:~/playground/anotherplace$ ln -s sauce target
zanna@toaster:~/playground/anotherplace$ file target
target: broken symbolic link to sauce

That symlink doesn't work because there's no sauce here. The path sauce isn't enough information. (From this point onward I've removed the user@host part of my prompt for easier reading, but I'm showing the part that indicates the current working directory as this shows how the commands work.). We can fix that by using an absolute path to make the symlink:

~/playground/anotherplace$ rm target
~/playground/anotherplace$ ls -s /home/zanna/playground/linkyland/sauce target
~/playground/anotherplace$ file target
target: symbolic link to /home/zanna/playground/linkyland/sauce

However, we could also fix it by making a correct relative path:

~/playground/anotherplace$ rm target 
~/playground/anotherplace$ ln -s ../linkyland/sauce target
~/playground/anotherplace$ file target
target: symbolic link to ../linkyland/sauce

So the idea that we need absolute paths is... just wrong. We need a correct path, absolute or relative.

If paths change, symlinks with absolute paths to files in the same directory break, but those with relative paths do not:

~/playground/anotherplace$ cd ../linkyland
~/playground/linkyland$ ln -s /home/zanna/playground/linkyland/sauce target2
~/playground/linkyland$ cd ..
~/playground$ mv linkyland elsewhere
~/playground$ file elsewhere/target*
elsewhere/target: symbolic link to sauce
elsewhere/target2: broken symbolic link to /home/zanna/playground/linkyland/sauce

So it is often preferable to use relative paths. However, if the location of the source file is unlikely to change, but the location of the symlink is likely to change, it would be preferable to use an absolute path:

~/playground$ cd anotherplace 
~/playground/anotherplace$ ln -s ../elsewhere/sauce target-rel
~/playground/anotherplace$ ln -s /home/zanna/playground/elsewhere/sauce target-abs
~/playground/anotherplace$ cd ..
~/playground$ mv anotherplace ..
~/playground$ cd ..
~$ file anotherplace/*
anotherplace/target-abs: symbolic link to /home/zanna/playground/elsewhere/sauce
anotherplace/target-rel: broken symbolic link to ../elsewhere/sauce

Solution 2

Symbolic links don't need absolute paths. It works just fine with relative paths:

$ ls -l /usr/bin/X11
lrwxrwxrwx 1 root root 1 May 11  2017 /usr/bin/X11 -> .

See, here's a symbolic link to a relative path, which works perfectly fine:

$ realpath /usr/bin/X11/yes
/usr/bin/yes
$ file /usr/bin/X11/yes
/usr/bin/X11/yes: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=add2c9ee02a98b5066d08d5ba2e79697880b2662, stripped

Solution 3

I believe I ran into the same issue as the poster and this was the first post I came to, I have since realized what I was doing wrong so will post here in case it helps others (at the time of writing no other answers or comments here mentioned the key piece of info I was after).

From the official docs for ln:

When creating a relative symlink in a different location than the current directory, the resolution of the symlink will be different than the resolution of the same string from the current directory. Therefore, many users prefer to first change directories to the location where the relative symlink will be created, so that tab-completion or other file resolution will find the same target as what will be placed in the symlink.

So the key thing to know is that what you write as your source/file/path is basically just placed into the link file verbatim, as text. When the link file is later interpreted, this text of the path is where your computer tries to navigate to from the directory of the symlink.

I was getting broken links because the directory I was running the command from (and using tab completion from) was different to the directory I was creating the link in. And the tab completion was working to give me a valid relative path but only from my current directory, that same relative path is no good in the context of interpreting it from any other directory (obviously because it's relative) including the other directory where I created the symlink.

My mistake was assuming that the ln command would do some clever reinterpretation of the path for me, I thought that since --relative is an option, that then if that option was not supplied, ln would resolve my relative tab completed path into an absolute path, but it doesn't work like that.

Playing around a bit more I've discovered that the --relative option is not about interpreting the link as a relative path or an absolute path, but actually it seems to grant the behavior I was originally expecting!

So I've now come to the understanding that if you use ln -s without --relative or -r then whatever you write for the path/to/sourcefile will be retained unchanged in the link file, and you can use a relative path here but whatever path you put will be interpreted in the context of from the directory that the symlink resides in.

If however you use ln -sr then you can use tab completion to navigate to your source file from your current directory even if you're not creating the symlink in that directory, and ln will do some clever stuff to translate that tab completed path you wrote in the command, into one that is valid when interpreted from the location of your symlink.

So for example:

alex@pc:/run/shm/training$ echo hello > sourcefile.txt
alex@pc:/run/shm/training$ ls
sourcefile.txt
alex@pc:/run/shm/training$ ln -s sourcefile.txt sameDirNotRelative
alex@pc:/run/shm/training$ ln -sr sourcefile.txt sameDirRelative
alex@pc:/run/shm/training$ cd ..
alex@pc:/run/shm$ ln -s training/sourcefile.txt training/oneUpNotRelative
alex@pc:/run/shm$ ln -sr training/sourcefile.txt training/oneUpRelative
alex@pc:/run/shm$ cd ..
alex@pc:/run$ ln -s shm/training/sourcefile.txt shm/training/twoUpNotRelative
alex@pc:/run$ ln -sr shm/training/sourcefile.txt shm/training/twoUpRelative
alex@pc:/run$ cd shm/training/
alex@pc:/run/shm/training$ ls -l
total 4
lrwxrwxrwx 1 alex alex 23 Jul  6 09:10 oneUpNotRelative -> training/sourcefile.txt
lrwxrwxrwx 1 alex alex 14 Jul  6 09:10 oneUpRelative -> sourcefile.txt
lrwxrwxrwx 1 alex alex 14 Jul  6 09:10 sameDirNotRelative -> sourcefile.txt
lrwxrwxrwx 1 alex alex 14 Jul  6 09:10 sameDirRelative -> sourcefile.txt
-rw-r--r-- 1 alex alex  6 Jul  6 09:08 sourcefile.txt
lrwxrwxrwx 1 alex alex 27 Jul  6 09:11 twoUpNotRelative -> shm/training/sourcefile.txt
lrwxrwxrwx 1 alex alex 14 Jul  6 09:11 twoUpRelative -> sourcefile.txt

Inspecting the resulting links reveals that two are broken, the ones created without the relative flag and from outside their resident directory (/run/shm/training), The non relative one created in the same training directory just happens to work because the path is valid when navigating from the resident directory of the link.

alex@pc:/run/shm/training$ file sameDirNotRelative 
sameDirNotRelative: symbolic link to sourcefile.txt
alex@pc:/run/shm/training$ file oneUpNotRelative 
oneUpNotRelative: broken symbolic link to training/sourcefile.txt
alex@pc:/run/shm/training$ file twoUpNotRelative 
twoUpNotRelative: broken symbolic link to shm/training/sourcefile.txt
Share:
5,006
cainiaofei
Author by

cainiaofei

Updated on September 18, 2022

Comments

  • cainiaofei
    cainiaofei over 1 year

    I made a symbolic link using the command ln -s source target.

    In the first attempt, I used a relative path and I ended up with a broken symbolic link...

    Searching online, I read that I have to use an absolute path. I am interested in why it needs to be an absolute path rather than a relative path, which is more convenient. I searched and didn't find an answer.

    • Admin
      Admin over 6 years
      ln doesn't need an absolute path. sh, for example, is a symlink to dash, which is in the same directory (/bin).
    • Admin
      Admin over 6 years
      I'm not sure what you're asking exactly, but maybe I'm just thick. What was the exact command you ran, and what was the exact error message you got?
    • Admin
      Admin over 6 years
      Rather than a posting a question with phrases such as "I entered a relative path" and saying "I didn't like the result" so "I entered an absolute path" and saying "I didn't like the result", it is better to copy and paste exactly what you entered and the error messages that appeared in the terminal. Then post the question about why what exactly happened when you expected something else. ie The Absolute could be Relative to some or the Relative may not be Absolute to others.
    • Admin
      Admin over 6 years
      Maybe the title should be "When does the "ln" command need an absolute path?" instead of "Why does "ln" command need an absolute path?". Just thinking here.
    • Admin
      Admin almost 3 years
      You'd think it would at least warn you or something...