1

From the manual of coreutils, 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.

The string stored in a relative symlink is determined completely by the source pathname and the target pathname, both specified as command line arguments to ln.

I don't see how the current directory gets involved. So I don't quite understand the reason why many users prefer to change the current directory to the parent directory of a to-be-created relative symlink before creating it. Could you rephrase or give some examples? Thanks.

  • They change directories to the location where the relative symlink will be created, so that their point of view when they create it, is the same as that when it is used. This reduces confusion. also when using tab completion, it makes it easier, as tab completion will not work otherwise. – ctrl-alt-delor Jul 3 '16 at 21:34
  • Still confused. The string stored in a relative symlink is determined completely by the source pathname and the target pathname, both specified as command line arguments to ln. I don't see how the current directory gets involved. – Tim Jul 3 '16 at 21:36
9

There are two things to remember:

  1. ln -s (without -r) stores the target name literally as you pass it to it
  2. if you pass a relative target, it resolves relatively to the link name, not your current working directory

Example:

I'm in /home/user/d0 and I want a link to /home/user/file so I do:

ln -s ../file .

../file is a valid path from d0.

Now if there's a subdirectory d1 (/home/user/d0/d1) and I want to place a link to ../file (/home/user/file) there without changing dirs, I need to do:

ln -s ../../file d1/ 

because the relative path needs to be relative to the link name, not my current working directory. ../../file (probably) resolves to nothing relative to d0 (unless there's a file named /home/file), so I won't get autocompletion for it which might make the operation more error prone.

So I change into d1 first:

cd d1
ln -s ../../file . 

and now ../../file makes sense relative to both the current directory and the link name; autocompletion kicks in, and I get my assurance I've got the right name.

GNU ln has a --relative|-r flag which makes this easier by saving you from having to compose these relative paths manually. With it, you can use a path relative to the current directory or an absolute path, and it'll relativize it relative to the link name (as it needs to be).

8

If you did

mkdir /tmp/foo
ln -s ../../etc/passwd /tmp/foo

Then access /tmp/foo/passwd would convert to /tmp/foo/../../etc/passwd - ie /etc/passwd

If you did

mkdir /var/tmp/foo
ln -s ../../etc/passwd /var/tmp/foo

Then access to /var/tmp/foo/passwd would convert to /var/tmp/foo/../../etc/passwd - ie /var/etc/passwd

So you can see the "relative symlink path" is resolved differently, depending on where the link is saved. The current directory you run the ln command in is not involved at all.

To avoid this confusion some users will change their current directory before creating the symlink

mkdir /tmp/foo
cd /tmp/foo
ln -s ../../etc/passwd
  • Thanks. I am using Ubuntu. After running ln -s ../aaa/bbb /tmp/output/, readlink output/bbb outputs ../aaa/bbb, instead of /tmp/output/../aaa/bbb in your example. Do I miss something? – Tim Jul 4 '16 at 1:20
  • 2
    readlink just reads the link, it doesn't resolve the link. Use realpath to expand all the links and relative paths. – Stephen Harris Jul 4 '16 at 2:07
4

You are absolutely correct. What goes in the symlink is exactly what is specified on the command. But the manual tries to say, that the symlink will be interpreted relative to the location of the link, so the user should take care to make the link correctly.

Take an example:

/tmp/test$ mkdir bar
/tmp/test$ ls -l foo/aaa 
-rw-r--r-- 1 itvirta itvirta 0 Jul  4 00:37 foo/aaa
/tmp/test$ ln -si foo/aaa bar/

The file is there, just where we think it is, so one might assume that the link works now. Except it doesn't, since the link is interpreted relative to its location, not relative to where I was when creating it.

/tmp/test$ cat bar/aaa 
cat: bar/aaa: No such file or directory

If I go to the target directory before creating the link, I'm sure to not get confused:

/tmp/test$ cd bar
/tmp/test/bar$ ls -l ../foo/aaa 
-rw-r--r-- 1 itvirta itvirta 0 Jul  4 00:37 ../foo/aaa
/tmp/test/bar$ ln -si ../foo/aaa  .

However, to add to the confusion, when creating a hard link, the way to point to the target file is exactly the obvious way that didn't work when creating a symlink:

/tmp/test$ ln foo/aaa bar/ 

There reminder is there because symlinks point by name, not by the file identity (inode) like pretty much all other actions would. Moving to the target directory first aligns both cases, and reduces the chance of human miss-ups.

1

When you type ln -sT abc/fgh mno/xyz it stores abc/fgh in a new file mno/xyz, and sets the link bit. When you look in mno/xyz you will actually look in mno/abc/fgh. This matters not (as you say) what directory you are in when you create, or use the link.

However:

They change directories to the location where the relative symlink will be created, so that their point of view when they create it, is the same as that when it is used. This reduces confusion. Also when using tab completion, it makes it easier, as tab completion will not work otherwise.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.