It’s been a long time since I knew that symlinks to directories are tricky. Recently I decided to look into it more closely and learn once and for all what is going on with them.
Here is what I learned. Assume the directory structure created by the following commands.
mkdir -p /tmp/a/b mkdir /tmp/z ln -s /tmp/a/b /tmp/z/y
And let’s move to the symlinked directory.
cd /tmp/z/y
Now a few interesting things happen. If you type cd ../
and press tab
, the directory name y
will be autocompleted. However, if you type ls ..
, you will see the directory b
– no trace of y
.
What is going on???
Well, let’s start with the easy part. The /tmp/z/y
directory is really /tmp/a/b
, so ..
in it points in reality to /tmp/a
. That’s why ls ..
showed the contents of /tmp/a
.
But the shell thinks that you are in /tmp/z/y
– for example, if your shell prompt is set up to show the current directory (which it is by default on most systems), it doesn’t show /tmp/a/b
but /tmp/z/y
. This means that if you type cd ..
and press enter
, it will strip one component from /tmp/z/y
and land you in /tmp/z
. A similar thing happens if you press tab
instead – it will suggest autocompletions from /tmp/z
.
This is not the whole story, though. bash
man page mentions a -P
option to cd
, which causes it to resolve symlinks before processing ..
entries. Thus, when in /tmp/z/y
, cd -P ..
will go to /tmp/a
. However, the same man page mentions also -L
, which “forces symbolic links to be followed by resolving the link after processing instances of ..~”. Does that mean that ~-L
is the default behavior? That is left a bit vague. Happily, man pages are not everything – there is also the info
manual, which does say explicitly that -L
is the default.
The takeaway from all of it is probably that it’s better to avoid symlinking directories, and if (for some reason) you must do that, I’d strongly consider writing some scripts or shell aliases to work with them so that you’ll never need to manually enter or leave them.
That’s it for today!