107

My basic understanding of a symlink is that it's 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?

Oli
  • 16,068
  • 1
    Your understanding is a bit limited; the only reason it's called a 'file' is because there's no better word for it. – Shadur-don't-feed-the-AI Aug 30 '13 at 09:23
  • 10
    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-don't-feed-the-AI Aug 30 '13 at 09: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

7 Answers7

56

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.

Oli
  • 16,068
  • 12
    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 07 '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 http://askubuntu.com/a/186227/69004 – sobi3ch Nov 18 '15 at 15:15
31

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.

NlightNFotis
  • 7,575
  • 12
    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 08: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 08: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 09: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 07 '14 at 14:13
10

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"
  • 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 07 '14 at 14:07
2

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"
}
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

one can edit the target value using 'rename -s'. an absolute path or an expression can be used.

synopsis:

rename -s old_target_or_expression new_target_or_expression link_name

example

$ ln -s /home/sr/tmp/bla foo # create a symlink
$ ls -l foo
lrwxrwxrwx 1 sr sr 16 Jun  4 14:41 foo -> /home/sr/tmp/bla
$ rename -s /home/sr/tmp/bla /home/jn/tmp3/blabla foo # replace the target using the absolute path
$ ls -l foo # it works!
lrwxrwxrwx 1 sr sr 20 Jun  4 14:43 foo -> /home/jn/tmp3/blabla
$ rename -s jn/tmp3/ mr/tmp4/ foo # replace the target using an expression (only a part of the string)
$ ls -l foo # it works again!
lrwxrwxrwx 1 sr sr 20 Jun  4 14:44 foo -> /home/mr/tmp4/blabla

man rename

-s, --symlink
           Do not rename a symlink but its target.
0

with Vifm

Vifm the file manager has a default key mapping (shortcut) to change where a symlink points to:

c+l

The mnemonic is change link.

Here's a cheetsheet that includes this mapping.