Is there a way to edit a symbolic link without deleting it first? [duplicate]
Asked Answered
L

10

87

So I created a symbolic link:

ln -s /location/to/link linkname

Now I want to change the location that the symlink links to. How do I do that? Is there a way to do it without deleting it first?

Lief answered 13/11, 2009 at 5:22 Comment(1)
I think you would get better response here: serverfault.comOutshine
D
65

You could create the new link with a different name, then move it to replace the old link.

ln -s /location/to/link linkname

Later

ln -s /location/to/link2 newlink
mv newlink linkname

If newlink and linkname are on the same physical device the mv should be atomic.

Danford answered 13/11, 2009 at 7:1 Comment(5)
@Lief - en.wikipedia.org/wiki/Atomic_operationDanford
FYI, i'm sure this works on some os but this didn't work for me, the move operation just removed the 'new' link instead of replacing the old one. i still had to rm first.Dotterel
@Dotterel You mean that mv newlink linkname caused the newlink file to be deleted, but didn't overwrite the linkname file? Did it do this silently? That seems extremely mysterious.Affected
mv newlink linkname will move your newlink into linkname if they are directories. So this method is not 100% perfect.Internee
@martinclayton @Internee Perhpas you should use mv -T instead of plain mv ?Imputable
S
30

Try ln -sf new_destination linkname.

Seringapatam answered 13/11, 2009 at 5:33 Comment(4)
This is non-atomic, though. See my answer for details. I'm not clear whether that's the poster's concern or not.Diction
This also won't work with symlinks pointing to directories. It will just create a new symlink inside the old target directory.Depressor
Use the -n (--no-dereference) switch to solve this.Thromboplastin
It would be better to update the answer (but it may be too late now).Kiel
N
29

Just change the symlink target:

# ln -sfT /path/to/new/target linkname

This is an instant, atomic change.

Norwood answered 18/2, 2015 at 5:8 Comment(4)
what is a T option? I don't have in my MacOSVacancy
-T, --no-target-directory treat LINK_NAME as a normal file always you can brew install coreutils and access the GNU versions with a g prefix, like gln if you want the GNU versions on macOS. saves a lot of headache when there are subtle differences like this.Philippic
What is implied here? As root?Kiel
-n can also work, and has the benefit of existing both on GNU ln and MacOS's ln.Uncrowned
T
17

If the symlink targets are directories, you need to add the -T flag to the mv command, otherwise it moves the new symlink in to the target directory of the old symlink.

Example of atomically switching a website to a new version:

Original setup - website is stored in www1 directory, vhost pointing at www symlink:

ln -s www1 www

Browse to website, see old version.

Put new website files in new www2 directory.

Set up new symlink to new website:

ln -s www_new www2

Move www symlink to directory of new website:

mv -T www_new www

Browse to website, see new version immediately.

Tarrsus answered 26/3, 2013 at 17:44 Comment(2)
So apparently the version of mv on my NAS's microkernel doesn't support the -T argument. Any alternative suggestions for doing this atomically?Hilburn
Try creating the symlink with the -f (force) flag. That seems to work. See the answer below: ln -sf new_destination linknameTarrsus
B
11

On OS X, the man page for ln says you can do it like this:

ln -shf /location/to/link link name

From the man page:

The options are as follows:
 -F    If the target file already exists and is a directory, then remove it so that the link may occur.  The -F
       option should be used with either -f or -i options.  If none is specified, -f is implied.  The -F option is
       a no-op unless -s option is specified.

 -h    If the target_file or target_dir is a symbolic link, do not follow it.  This is most useful with the -f
       option, to replace a symlink which may point to a directory.

 -f    If the target file already exists, then unlink it so that the link may occur.  (The -f option overrides any
       previous -i options.)

 -i    Cause ln to write a prompt to standard error if the target file exists.  If the response from the standard
       input begins with the character `y' or `Y', then unlink the target file so that the link may occur.  Other-
       wise, do not attempt the link.  (The -i option overrides any previous -f options.)

 -n    Same as -h, for compatibility with other ln implementations.

 -s    Create a symbolic link.

 -v    Cause ln to be verbose, showing files as they are processed.
Byrdie answered 17/2, 2014 at 21:30 Comment(1)
What version of OS X? The default shell was changed to Z shell in macOS v10.15 (Catalina. 2019-10-07), if it matters.Kiel
F
7

For directories, you want to do: ln -sfT /location/to/new/target old_linkname

Frohne answered 2/4, 2014 at 21:48 Comment(2)
this is a much better solution than having to call a second commandAllayne
Isn't this already covered in previous answers?Kiel
D
5

No. The symlink system call will return EEXIST if newpath already exists. You can only link from a new node in the filesystem. What's the requirement here? If you're worried about a race due to the non-atomicity of the unlink/symlink calls, then you might want to rethink the architecture a little to provide synchronization elsewhere. There have been some scary security bugs introduced by this kind of thing.

Diction answered 13/11, 2009 at 5:33 Comment(0)
A
4

As others have mentioned, you basically have to delete the symlink first, either manually or by passing the -f flag to the ln utility.

Years ago, I had to make small edits to symlinks pretty frequently, so I wrote a simple readline-based utility (edln) to make this less annoying. In case anyone else finds it useful, I've put it online at https://github.com/jjlin/edln/.

edln will display the original symlink target; you can then use the arrow keys, or standard readline keystrokes (M-b, M-f, C-d, etc.) to move around and edit the target.

Archon answered 7/5, 2012 at 0:46 Comment(5)
thank you! i could compile and run it on openbsd, the makefile misses install target though :-)Windtight
Just a note that, at least with today's bash, you can use read built-in's readline support: read -e -p 'New: ' -i "$(readlink -- "$1")" lnkGonad
It would be quite nice to have a version of edln implemented as a bash script, but readline behavior doesn't appear to be as customizable via bash. I gave it a shot, but I haven't figured out how to avoid undesirable behavior like shell-escaped filename completions.Archon
(touch 'a b'; IFS= read -e -p 'New: ' -i ./ lnk; echo ":$lnk:"; ls "$lnk") seems to work for me, read -e undoes the escapes; there's just a trailing whitespace after pressing <TAB> the user must deleteGonad
Ah... the key is not passing -r to read -- with all its implications. Then tab-completion adds escapes, which are undone by read's default behaviorGonad
C
3

Chain the commands like this:

rm currentlink && ln -s /path/to/link currentlink

The first command removes the existing one and the 2nd immediately creates it again.

Congregationalism answered 26/2, 2013 at 9:20 Comment(0)
R
1

Just googled, found no good answer and had to solve myself:

ln -f -s -T `readlink SomeLibrary | sed 's/version.old/version.new/'` SomeLibrary

Editing by definition means not recreating from scratch but changing partly. Any answer requiring to memorize a path, maybe long or with weird symbols, is definitely bad.

Reconcile answered 29/8, 2014 at 11:32 Comment(1)
What do you mean by "memorize a path"? Why would that be necessary? Can you provide an example of that?Kiel

© 2022 - 2024 — McMap. All rights reserved.