78

My basic understanding of a symlink is as a special file, a file that contains a string path to another file. The kernel's VFS abstracts a lot of that away but is there any reason why symlinks seem to be impossible to edit?

In other words: Can I edit a symlink? If not, why not?


I do understand that there are various ways of replacing symlinks (two alternatives are currently in the answers section) but it would be interesting to get an explanation on why replacement seems to be the only way to deal with symlinks. Why can't you just change where they point?

3
  • Your understanding is a bit limited; the only reason it's called a 'file' is because there's no better word for it. – Shadur Aug 30 '13 at 9:23
  • 7
    Unlike the embarrassing atrocity that is the windows variant, posix-style symbolic links operate on/in the filesystem layer itself. The only way to edit one would be to edit the filesystem directly -- and it's generally not worth it. – Shadur Aug 30 '13 at 9:27
  • 1
    @Shadur .lnk files are not really symlinks to begin with (and NTFS has had proper symlinks since Vista); they're more like shortcuts for executing commands, whether that is changing to a specific folder or starting a program with specific arguments and with a certain CWD. – JAB Apr 12 '16 at 20:11
43

Given that -f just does a silent replacement, you can do an atomic replacement with mv -T (-T makes it work even if /loc.../link is a directory):

ln -s /location/to/link linkname
# ... 
ln -s /location/to/link2 newlink
mv -T newlink linkname

linkname is accessible throughout the process.

5
  • 9
    That does get you the atomic replacement, though you still are doing a replace rather than edit ( new link has a new inode number ). – psusi Aug 30 '13 at 13:28
  • 2
    @psusi I completely agree, it's just a technically slightly better option than the other answer in some scenarios. – Oli Aug 30 '13 at 13:34
  • If you're redirecting the link to another destination then changing it's inode number seems like a small change. – ams Aug 30 '13 at 13:58
  • 4
    That assumes linkname is not a symlink to a directory. Use the -T option to mv if on GNU or -h if on FreeBSD to avoid that. Note that like ln -sf that does not preserve the permissions of the link (on systems where they are significant). – Stéphane Chazelas Nov 7 '14 at 14:48
  • Another solution for change symlink for directory is to use -n options e.g.: ln -sfn DESTINATION_DIRECTORY LINK_NAME . Read more on askubuntu.com/a/186227/69004 – sobi3ch Nov 18 '15 at 15:15
26

If by edit, you mean to change the file it points to, then yes you can:

$ ln -s .bashrc test
$ ls -al test
lrwxrwxrwx 1 pascal pascal 7 2009-09-23 17:12 test -> .bashrc
$ ln -s .profile test
ln: creating symbolic link `test': File exists
$ ln -s -f .profile test
$ ls -al test
lrwxrwxrwx 1 pascal pascal 8 2009-09-23 17:12 test -> .profile

The -f parameter (--force) when passed to ln it causes it to call the unlink() system call right before symlink()

Taken from the following stack overflow answer.

4
  • 10
    I think it's questionable if this can be regarded as "edit", since unlink();symlink(); is not atomic, so there is a tiny tiny amount of time during which the link does not exist. – replay Aug 30 '13 at 8:15
  • 2
    @mauro.stettler Yes, you are right. But I guess that this depends on your perspective. If you take only the end result into account, then maybe you could consider it as edited, no other things considered. – NlightNFotis Aug 30 '13 at 8:17
  • 1
    The quoted section from the Unix Time Sharing System paper describes hard links. These are entirely different from the symbolic links (symlinks) the OP asked about. – Ansgar Esztermann Aug 30 '13 at 9:00
  • 6
    Note that it assumes test's target is not a directory. Otherwise ln -s -f .profile test would create a .profile symlink in that directory. GNU ln has a -T option to avoid that. – Stéphane Chazelas Nov 7 '14 at 14:13
11

Symbolic links need to be modified atomically. If you're halfway through writing them, they won't work. The content of a symbolic link is pretty small (at most 4095 characters on Linux: the maximum length of a path to a file), so there would be little point in editing part of a symbolic link at the kernel level. Therefore the kernel does not offer any interface to edit a symbolic link, only an interface to create a new one, the symlink system call (plus the generic interface unlink to remove any file).

The symlink system call only creates a new symbolic link, it does not remove any existing file. This is annoying, but consistent with other system calls to create files such as open (which can create a new file or truncate an existing file, but not replace an existing file by a newly-created file) and mkdir.

In the shell, as you've discovered, while you can't replace a symbolic link atomically with the ln command (ln -sf unlinks the previous file then creates the symbolic link), you can do it by first creating a symbolic link under a temporary name and then moving it into place.

tmp=$(TMPDIR=$(dirname -- "$link") mktemp)
ln -sf -- "$target" "$tmp"
mv -f "$tmp" "$link"
1
  • 2
    mv -f (like ln -sf) will not do what you want if $link points to a directory. GNU ln and mv have a -T for that. mv (rename system call) will always change the inode of $link while ln -sfT (unlink+symlink) might reuse the same. – Stéphane Chazelas Nov 7 '14 at 14:07
2

Assume linkname exists as the result of having done (in the past):

 ln -s   /the/path/to/a/file   linkname

Then, there are three ways to change the symlink:

  • Use ln with -f force and even for directories -n (inode could get reused):

    ln -sfn /some/new/path linkname
    
  • Remove the symlink and create a new one (even for directories):

    rm linkname; ln -s /some/new/path linkname
    
  • create a new symlink, then mv it (atomic change even for directories):

    ln -s  /some/new/path newlinkname
    mv -fT newlinkname linkname             # linkname remains after the command
    
1

Technically, there's no built-in command to edit an existing symbolic link. It can be easily achieved with a few short commands.

Here's a little bash/zsh function I wrote to update an existing symbolic link:

# -----------------------------------------
# Edit an existing symbolic link
#
# @1 = Name of symbolic link to edit
# @2 = Full destination path to update existing symlink with 
# -----------------------------------------
function edit-symlink () {
    if [ -z "$1" ]; then
        echo "Name of symbolic link you would like to edit:"
        read LINK
    else
        LINK="$1"
    fi
    LINKTMP="$LINK-tmp"
    if [ -z "$2" ]; then
        echo "Full destination path to update existing symlink with:"
        read DEST
    else
        DEST="$2"
    fi
    ln -s $DEST $LINKTMP
    rm $LINK
    mv $LINKTMP $LINK
    printf "Updated $LINK to point to new destination -> $DEST"
}
5
  • Sorry, but you “answer” is only remotely-related to the question asked. – user2233709 Nov 26 '17 at 23:01
  • 1
    Hi @user2233709 - the user's question "How can I edit symlinks?" was clearly answered in the first sentence as well as by providing a solution. Would you care to elaborate? – blizzrdof77 Nov 30 '17 at 19:38
  • 3
    Did you really read the question? It’s about whether it is possible to modify a symlink, rather than replace it. Your proposed “solution” is a script that replaces a symlink. – user2233709 Nov 30 '17 at 22:47
  • 1
    Saying that this answer “is only remotely-related to the question asked” is unduly harsh.  blizzrdof77 says that it’s impossible to do what the question asks for; that is an acceptable answer.  “Your proposed “solution” … replaces a symlink.”  The same is true for all the other answers (except for the one that ‘‘actually shows’’ how to edit a symlink, which is wrong). – Scott Dec 3 '20 at 0:22
  • The bad news is that this is just a more verbose, less careful version of Gilles’s answer. – Scott Dec 3 '20 at 0:22
0

I found a link file "gap" in my ~/bin folder, must have been placed there by some installation process (I never used the ln command myself). I wanted to upgrade from gap-4.10.2 to gap-4.11.0, so I opened the link file using

vim gap

but it opened in read-only mode, so I opened it with

sudo vim gap

and then I could change it with insert mode.

I just edited the content of the link file from

#!/bin/sh

GAP_EXE=$GAP_DIR
if [ "x$GAP_DIR" = "x" ]; then
  GAP_DIR=$(cd "/home/user/gap-4.10.2" && pwd)
  GAP_EXE=/home/user/gap-4.10.2
fi

exec "$GAP_EXE/gap" -l "$GAP_DIR;/home/user/.gap" "$@"

by replacing both numbers by 4.11.0 respectively (a path that already existed), so that it looked like

#!/bin/sh

GAP_EXE=$GAP_DIR
if [ "x$GAP_DIR" = "x" ]; then
  GAP_DIR=$(cd "/home/user/gap-4.11.0" && pwd)
  GAP_EXE=/home/user/gap-4.11.0
fi

exec "$GAP_EXE/gap" -l "$GAP_DIR;/home/user/.gap" "$@"

Then save&exit with

:wq

So, YES, you CAN edit a link file.

One problem still remains: in /bin with

ls -l

the name is still

gap -> ../gap-4.10.2/bin/gap.sh

so the long name of the link file still has the old path in it, but it points to the new path. Is there any way to change that identifyer? Not that it's really bothering me. The link works fine now.

2
  • 1
    The question is exactly about editing the ../gap-4.10.2/bin/gap.sh part. By “editing” gap, you’re really editing ../gap-4.10.2/bin/gap.sh; you’re not editing the link. – Stephen Kitt Aug 13 '20 at 14:06
  • Also, you start out saying that you’re talking about ~ /bin, but then you end up saying that you’re talking about /bin. – Scott Dec 2 '20 at 21:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.