Welcome to End Point’s blog

Ongoing observations by End Point people

Using ln -sf to replace a symlink to a directory

When you want to forcibly replace a symbolic link on some kind of Unix (here I'm using the version of ln from GNU coreutils), you can do it the manual way:

rm -f /path/to/symlink
ln -s /new/target /path/to/symlink

Or you can provide the -f argument to ln to have it replace the existing symlink automatically:

ln -sf /new/target /path/to/symlink

(I was hoping this would be an atomic action such that there's no brief period when /path/to/symlink doesn't exist, as when mv moves a file over top of an existing file. But it's not. Behind the scenes it tries to create the symlink, fails because a file already exists, then unlinks the existing file and finally creates the symlink.)

Anyway, that's convenient, but I ran into a gotcha which was confusing. If the existing symlink you're trying to replace points to a directory, the above actually creates a symlink inside the dereferenced directory the old symlink points to. (Or fails if the referent is invalid.)

To replace an existing directory symlink, use the -n argument to ln:

ln -sfn /new/target /path/to/symlink

That's always what I have wanted it to do, so I need to remember the -n.


Hrish said...

Thanks - that was helpful!

Jon Jensen said...

I just came across the article Things UNIX can do atomically which has many useful notes along these lines.

In particular, there's "ln -T" which is similar to "ln -n" (see the manpage) but more importantly, "mv -T" for atomically moving a new symlink into place. It just has to be created as a separate file first. As I noted above, ln -s isn't atomic.

jeremyrdavis said...

Very useful, thanks! I mentioned this post from my blog. Maybe it will help your search results.

Jon Jensen said...

Jeremy, thanks for letting me know it was helpful!

Cyrus said...

Thank you!

tchnts said...

`ln -sfn` or `1n -sfT` are not atomic. Look at strace

strace ln -Tsf 3_l 1_l
symlink("3_l", "1_l") = -1 EEXIST (File exists)
unlink("1_l") = 0
symlink("3_l", "1_l") = 0

Jon Jensen said...

tchnts: Yes, above in both the article and a comment, I clearly stated that ln is not atomic. I claimed that based on strace output as well.

mv is atomic.

Marcos Santos said...

It doesn't work:

" cannot overwrite directory"

Jon Jensen said...

Marcos, it certainly has worked for me and others.

What OS & version are you using? Which version of GNU coreutils or other ln? Are you sure you're not trying to overwrite an actual directory vs. a symlink to one?

Ketan said...

Thanks for this post. I was missing the "-n" and was running into permissions errors.

Daniel Kislyuk said...

thanks, that was helpful