Beware of the symlink

Pascal Morin
Code Enigma
Published in
2 min readJan 24, 2018

Over the years, I keep on witnessing myself -and others- hitting this on a regular basis, so thought I would outline here a small oddity on how the “current” directory behaves in regards to symlinks.

TL;DR If you “cd” to a path containing a symlink, your current directory won’t change if the symlink gets updated to point to a different target.

Typically, you would ssh into a server, “cd” to your project’s directory to run a command, realize you need to push some changes, trigger a new build using your CI system, and … “WTF? Why aren’t the changes there ?”

A lot of deployment scripts work by pointing a symlink to what is the latest build, which is quite handy and make reverting to previous build easy. It looks something along the lines of:

/var/www/live.project.master -> /var/www/project_master_build_19

And the symlink gets updated for each new build:

/var/www/live.project.master -> /var/www/project_master_build_20

And so on …

The gotcha here is that if you `cd /var/www/live.project.master`, then trigger a build, you will still “operate” in the old build.

This is fairly trivial in the context of this example and bear no consequences but a bit of frustration. But when overlooked within complex scripts, this behaviour can lead to bugs that are difficult to track down.

The easiest way to understand how this works is simply to reproduce it. Open a new shell, and create two directories, then point a symlink to one of them:

$ mkdir ~/first_dir 
$ mkdir ~/second_dir
$ ln -s ~/first_dir ~/current_dir
$ cd ~/current_dir

You are now in the “current_dir” symlink, pointing to the the “first_dir” directory, right ?

$ pwd
/Users/pascal/current_dir
$ pwd -r
/Users/pascal/first_dir

If you next change the symlink’s target (or it gets changed by another user, session, script, …) without actually changing your current directory:

$ rm ~/current_dir
$ ln -s ~/second_dir ~/current_dir

you are still “in” the initial target, even though the symlink has been updated, as shown by `pwd -r` (Mac/BSD) or `pwd -P` (Linux):

$ pwd
/Users/pascal/current_dir
$ ls -l /Users/pascal/current_dir
/Users/pascal/current_dir -> /Users/pascal/second_dir
$ pwd -r
/Users/pascal/first_dir

While it is pretty logical once you know and think about it, it can be very confusing if you are not aware of the behaviour in the first place.

I suspect the only tips I can offer here as a way of conclusion are :

  • Use “pwd -r” in place of just “pwd” whenever you have a doubt.
  • You can quickly update your current directory by moving ‘in-place’ with “cd `pwd`”.

--

--