They are not equivalent.
In:
ln -sfn source dest
The -n
option to not dereference the destination is a non-standard extension (I believe from GNU ln
, added there in version 3.10 in 1994). Some other ln
s have -h
for the same feature (such as NetBSD since 1997), and on some others, like Solaris/Illumos -n
is for something else (predates GNU ln
's -n
).
Assuming the GNU behaviour of -n
, if dest
exists as a directory, that creates a dest/source
file replacing a file by that same name if any as long as it's not itself a directory.
Otherwise, if creates a dest
symlink to source
replacing the existing file if any.
ln
like cp
or mv
have that annoying ambiguous interface where they can do either move/copy/link to the destination or into the destination but that's decided based on the type of the last argument (or number of arguments) as opposed to what the user wants.
In:
rm dest
ln -s source dest
rm
will attempt to remove a dest
, possibly prompting the user under some conditions, and regardless of whether rm
succeeded or not will run ln -s source dest
which will create either dest/source
(if dest
is a directory which rm
could not have removed or a symlink to a directory which rm
may have been unable to remove for instance because you don't have write access to the current directory), or dest
as a symlink to source (failing if a non-directory file exits by that name).
With:
unlink dest
ln -s source dest
Same except for the prompting part and for the fact that on some systems and under some conditions, unlink
may be able to unlink a directory. unlink
is also flagged under the XSI option in the POSIX specification and will not be as widely available as rm
.
So if the intention is to try to create a dest
symlink to source
, you rather want:
GNU specific:
ln -sfT source dest
(where -T
disables the into mode, treating dest
as the intended file to create regardless of whether it's a directory or not).
Portable:
rm -f dest && ln -sf source dest
rm
with -f
does not prompt the user and does not fail if the file wasn't there in the first place. When it succeeds, you know the file is no longer there (or at least wasn't at the time rm
checked after it succeeded to remove it). Beware however that ln
could still create a source/dest
symlink if someone else (re)created a dest
directory or symlink to directory in-between those rm
and ln
.
ln
implementation at least may attempt an atomic replace (by renaming a new symlink on top of the old, since thesymlink()
syscall itself will fail if the target already exists). No surprise, the GNU coreutils implementation seems to do exactly that, while the Busybox one just does the simpleunlink()
+symlink()
combination – ilkkachu May 30 '23 at 15:34