20

I would like to change the target of symbolic link from within a bash script. The problem is that the symlink is quite important (it's /bin/sh, namely) and I would to do it in fashion that:

  1. New target will be available immediately after removing old, i.e. there will be no possibility that something will notice disappearing of it,
  2. There will be no possibility that the change will fail in the middle, i.e. leaving user with symlink removed and no new one.

I thought about two methods. Either using plain ln:

ln -fs /bin/bash /bin/sh

or using mv:

ln -s /bin/bash /bin/sh.new
mv /bin/sh.new /bin/sh

Which one will suit my needs better? Is there any possibility that one of them would try to replace the symlink target instead of symlink itself?

Michał Górny
  • 18,713
  • 5
  • 53
  • 76

2 Answers2

23

Renaming (mv) is an atomic operation; creating a new symlink is not (delete old symlink; create new one). So you should use mv:

$ ln -s new current_tmp && mv -Tf current_tmp current

Here's a blog post discussing this. Also, if you're worried about what will happen, why not try it on a non-critical symlink first?

robla
  • 133
  • 6
ire_and_curses
  • 68,372
  • 23
  • 116
  • 141
  • 1
    You can't be sure there will be no potential problems if you just try it :). It is very hard to make a test for "something notice disappearing ". – Eugene Sep 06 '09 at 09:15
  • 1
    About the blog post — `-T` doesn't seem to be a portable option. – Michał Górny Sep 06 '09 at 09:28
-1

It looks like (from the man page) ln -f unlinks the symlink before making the new one, which means mv is the better option for this.

I would, however, strongly recommend against linking /bin/sh to bash. Many scripts use:

#!/bin/sh

and are written assuming that the shell is the classic Bourne shell. If this were to run bash instead, you could easily get obscure incompatibilities between what the script assumes sh does and what bash actually does. These will be nearly impossible to track down.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • 5
    Except of course for the fact that on many systems, /bin/sh *is* a symlink to /bin/bash; and when invoked through it, bash will enter sh compatibility mode. – Williham Totland Sep 06 '09 at 09:10
  • bash cannot be sh compatible. Try this in a real sh and in bash: i=0;ls|while read a ; do i=expr $i + 1 ; done ; echo $i If you do not have a real sh, try with ksh Note that the above result even differs among sh versions. On solaris the extremely old sh prints 0, whereas the xpg4 version gives non zero (like ksh) – Gunstick Oct 15 '09 at 08:37
  • 1
    @Gun: so, you reason that since "sh" cannot be "sh"-compatible ('s what you're saying)... yeah, sure, then nothing, including bash, can be compatible with all the undefined behavior of old "sh"s. What bash *can* be, though, is be compatible with some commonly assumed/accepted/defined sh-standard. POSIX or SUSv2 comes to mind. – Jürgen A. Erhard Apr 15 '11 at 14:23
  • The intent was eselect-sh for Gentoo which allows user to choose which shell is linked to /bin/sh. It's user choice whether that's bash, dash... – Michał Górny Apr 21 '12 at 21:05