Linux symlinks: creating, deleting, and the gotchas worth knowing

The basic command for creating a symbolic link (a “symlink” or “soft link”) to a folder or file is ln -s:

1
2
3
4
5
6
7
8
ln -s /home/ronald/somefolder /home/ronald/newfolder

# or if your current directory is already /home/ronald:
cd /home/ronald
ln -s somefolder newfolder

# general form:
ln -s existingSourceFolder newLinkedFolder

The first argument is the target (what the link points at), the second is the link name (what gets created). I get this order backwards every time — the mnemonic that finally stuck is “ln -s is like cp: source first, destination second.”

To remove all symlinks in the current directory (and only symlinks — leaving regular files and directories untouched):

1
find . -maxdepth 1 -type l -exec rm -f {} \;

-type l is the filter that matches symlinks specifically. -maxdepth 1 keeps the search from descending into subdirectories, so you only kill the symlinks at the top level.


A few useful additions: symlinks have sharp edges.

Symlinks look simple — “a file that points to another file” — but they have a handful of behaviors that catch people out. The ones below are the ones I’ve actually been bitten by.

Symlinks vs. hard links. The -s in ln -s is the difference. Without it, you get a hard link: a second directory entry pointing to the same inode. Hard links don’t break if you move or rename the original (because both names refer to the same data). Symlinks store a path and break if the target moves. Hard links can’t span filesystems and can’t point to directories (the kernel forbids it to prevent loops); symlinks can do both.

Inspecting a symlink. A few different tools answer different questions:

1
2
3
4
5
ls -l newfolder              # shows: newfolder -> /home/ronald/somefolder
readlink newfolder           # prints just the target: /home/ronald/somefolder
readlink -f newfolder        # follows all symlinks, prints absolute target
realpath newfolder           # same idea as readlink -f, slightly different edge cases
stat newfolder               # full metadata; "File: newfolder -> ..."

readlink -f is the one I reach for most — it tells you where a symlink ultimately resolves, even through chains of symlinks-pointing-to-symlinks.

Relative vs. absolute targets. The path you pass as the target is stored verbatim. If you write a relative target, it’s resolved relative to the directory the link lives in, not the directory you ran the command from. This trips people up:

1
2
3
4
5
6
cd /tmp
ln -s ../etc/hosts mylink         # mylink points to ../etc/hosts
                                  # which from /tmp resolves to /etc/hosts ✓

mv mylink /var/log/               # now /var/log/mylink points to ../etc/hosts
                                  # which resolves to /var/etc/hosts ✗ broken

Rule of thumb: use absolute paths unless you specifically want the link to follow when both source and link move together (common pattern: shipping a relative symlink inside a tarball or git repo).

The rm -rf on a symlink-to-directory trap. This one is genuinely dangerous:

1
2
3
4
ln -s /important/data ./mydata
rm -rf mydata                # removes the symlink only — /important/data is safe
rm -rf mydata/               # WITH trailing slash: rm follows the link and
                             # recursively deletes /important/data

The trailing slash changes the command from “delete this symlink” to “delete what’s inside the directory the symlink points to.” If you’re in the habit of tab-completing paths, your shell often appends the slash for you. Be deliberate when removing symlinks to directories.

Updating an existing symlink. By default ln -s refuses to overwrite. The flags vary depending on what you’re replacing:

1
2
3
4
ln -sf newtarget mylink           # works for symlinks to files
ln -sfn newtarget mydirlink       # for symlinks to directories — without -n,
                                  # ln creates a NEW link INSIDE the existing
                                  # directory link instead of replacing it

Forgetting -n on a directory symlink is one of those bugs that’s invisible until you wonder why your link “didn’t take” — and meanwhile a stray newtarget entry is hiding in the linked directory.

Finding broken symlinks. Symlinks happily survive their targets being deleted. To find ones that no longer point anywhere valid:

1
2
find . -xtype l                          # broken symlinks under current dir
find / -xtype l 2>/dev/null              # the same, system-wide (slow)

-xtype l means “the symlink’s target is not a real file/directory we can stat” — i.e. the link is dangling. Useful as a periodic sanity check after you’ve moved or removed a directory that other things might have linked to.

Finding what links to a given target. The reverse question — “is anything pointing at this file?” — has no fast answer; you have to scan:

1
2
find / -lname "*somefolder*" 2>/dev/null    # by target pattern
find / -samefile /home/ronald/somefolder 2>/dev/null  # exact same inode

The first form matches the symlink’s stored target string; the second works for hard links (same inode). There’s no equivalent of “give me everything that resolves to this path,” because resolving every symlink on the system would be expensive — you’d be running readlink -f on every link in the search tree.

This entry was posted in Linux, Operating System, Ubuntu. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *


× 9 = twenty seven