ln

Two names for one file, or a pointer to a path

Updated 2026-09-05

ln -s makes a symbolic link: a small file holding a path as text. Nothing checks that path when the link is made or afterwards, so a symlink can point at something that does not exist, and a relative target is read from the directory holding the link rather than from wherever you were standing when you typed it. That single rule is behind most broken links.

ln without -s makes a hard link, which is a second name for the same data. The two names are equal: neither is the original, and the data survives until both are gone. It cannot cross a filesystem and cannot point at a directory.

The difference shows the moment a target is replaced. A symlink resolves the name again and finds whatever is there now; a hard link is still attached to the data it was made from.

Sample files used on this page

Every example below was run against these files. Recreate them to follow along.

site the tree the cp, mv and rm pages use as well; latest.css is already a symlink to style.css, and assets/ is the subdirectory the relative-target examples make a link in

site:
total 16
drwxr-xr-x 2 user user 4096 Jun 15 10:00 assets
drwxr-xr-x 2 user user 4096 Jun 15 10:00 backups
-rw-r--r-- 1 user user   36 Jun  1 09:00 index.html
lrwxrwxrwx 1 user user    9 Jun 18 09:00 latest.css -> style.css
-rw-r--r-- 1 user user   33 Jun 20 09:00 style.css

site/assets:
total 4
-rw-r--r-- 1 user user 35 Jun 10 09:00 logo.svg

site/backups:
total 0

notes.txt one line, beside site/ rather than inside it

Rebuild before deploying.
25 outputs, collapsed by default

ln -s target linkname, in that order. The target is written into the link as text and nothing verifies it.

A relative target is resolved from the directory holding the link, not from the directory you ran ln in. The two are the same often enough that the rule goes unnoticed until they are not.

ln with no -s gives the same data a second name. Neither name is the original, and the file survives until the last one is removed.

Choosing between them

The two behave alike until the target is replaced, which is the moment to understand before picking one.

Replace the target and watch them disagree

ln notes.txt hard.txt && ln -s notes.txt soft.txt && printf "replaced\n" > new.txt && mv new.txt notes.txt && echo "-- hard:" && cat hard.txt && echo "-- soft:" && cat soft.txt

mv put a different file at the name notes.txt. The hard link is attached to the data and still has the old contents; the symlink resolves the name again and finds the new ones. This is the whole difference.

Show output
-- hard:
Rebuild before deploying.
-- soft:
replaced

Test which kind you are holding

ln -s site/style.css link.css && [ -L link.css ] && echo "it is a symlink"; [ -f link.css ] && echo "and -f follows it to a regular file"

-L is the only test that does not follow the link. A hard link answers no to -L, because from the filesystem's point of view it is simply the file.

Show output
it is a symlink
and -f follows it to a regular file

Resolve a link to the real path

ln -s site/style.css link.css && basename "$(readlink -f link.css)"

readlink -f follows every link in the path and returns an absolute answer, where a bare readlink gives you the one string the link holds. basename is here because the absolute path depends on where you are.

Show output
style.css

Count the links in a tree

find site -type l | sort

-type l matches the link itself rather than following it. Sorting matters because find walks directory order, which is not an order you can predict.

Show output
site/latest.css

The current -> releases/v3 arrangement is how a deploy switches versions. Repointing that link has a trap in it.