Skip to content
VLSI Mentor

Linux & Bash · Part I · Module 6 · Links and File Identity

Following Links Deliberately: realpath, readlink, -L and -P

Module 2 established that every process sits in exactly one directory and that pwd reports it. That was true and, it turns out, incomplete — because once symbolic links exist, there are two different correct answers to where a path leads, and a surprising number of commands quietly pick one without telling you. The route you took through the tree and the place the data actually sits are both real, both useful, and sometimes different directories entirely. This chapter gives you the commands that answer each question, and the one demonstration that makes the distinction impossible to forget: two dots meaning two different things depending on who is reading them.

Intermediate16 min readLinuxBashPart IFundamentalsFilesystemLinks

Part I · Module 6 · Chapter 6.4 · Following Links Deliberately

1. Two Correct Answers

Set up a tree where the two answers differ — this matters, because in simple cases they coincide and the distinction looks academic:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
pp
├── releases
│   └── v3
│       └── rtl
└── work
    └── current -> ../releases/v3

work/current is a symbolic link to releases/v3. Now walk into it:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ cd pp/work/current

$ pwd
/home/engineer/pp/work/current

$ pwd -P
/home/engineer/pp/releases/v3

Two answers, both correct, for the same shell at the same moment.

  • pwd reports the logical path — the route you took, with the link still in it.
  • pwd -P reports the physical path — where the data actually sits, every link resolved.

Chapter 2.4 said pwd always reports an absolute path and left it there. Both of these are absolute. They name the same directory by two different routes.

After entering a directory through a symbolic link, two answers exist. The logical view remembers the route you took, so the working directory is reported with the link in it and going up one level returns to where you came from. The physical view reports where the data actually is, so going up one level lands in the target's real parent instead.The route you tookpwd — keeps the link in thepathBash remembers itwork/currentGoing up lands inwork — where you came fromWhere the data ispwd -P — resolves every linkThe kernel knows onlythisreleases/v3Going up lands inreleases — the real parent12
Figure 1 — after entering a directory through a symbolic link, the two views disagree about where you are and about what lies one level up. Neither is wrong; they answer different questions. The logical view is Bash remembering the route you walked, and the physical view is where the filesystem actually put the data.

2. Where the Two Answers Come From

The reason there are two answers at all:

Bash remembers the route you walked. The kernel does not.

When you cd through a link, Bash records the path you typed, including the link. The kernel, meanwhile, resolved the link immediately and put your process in the target directory — and keeps no memory of how you got there.

So pwd is Bash telling you what it remembers, and pwd -P is Bash asking the kernel. Both answer honestly; they have different information.

This is not a quirk of pwd. It is a choice every tool has to make, and the convention for expressing it is the pair Chapter 6.3 introduced:

OptionMeans
-Llogical — keep links in the path, follow the route
-Pphysical — resolve every link to where it really leads

Logical is usually the default, because it matches what people expect.

3. The Demonstration That Settles It

Where this stops being philosophy. Carry on from §1:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ pwd
/home/engineer/pp/work/current

$ cd ..

$ pwd
/home/engineer/pp/work

You went up and landed in work — the directory you came from. That is the logical answer, and it is almost certainly what you wanted.

Now the same move, physically:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ cd /home/engineer/pp/work/current

$ cd -P ..

$ pwd
/home/engineer/pp/releases

A different directory. releases, because the data really lives in releases/v3 and that directory's real parent is releases.

Both are "up one level". They disagree because there are two trees in play: the one you walked and the one on disk.

The detail that makes it unforgettable

Two dots do not always mean the same thing, and it depends on who is interpreting them:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ cd /home/engineer/pp/work

$ ls current/..
v3

$ realpath current/..
/home/engineer/pp/releases

cd .. landed in work. But ls current/.. listed v3 — the contents of releases — because this time Bash was not involved in resolving the dots. The path current/.. was handed to ls, and the kernel resolved it physically: into the link, then up, arriving at releases.

So:

  • cd .. uses Bash's memory → the logical parent.
  • current/.. inside a path is resolved by the kernel → the physical parent.

Chapter 2.3 said .. is a real directory entry that the filesystem resolves. That remains exactly true, and this is its consequence: when Bash is tracking your route it can override that, and when it is not, the entry wins.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ ls -l current
lrwxr-xr-x 1 engineer engineer 14 Mar 28 14:02 current -> ../releases/v3

$ readlink current
../releases/v3

readlink prints the text stored in the link and nothing more. Chapter 6.3 established that a symlink's contents are a path as text; this is the command that reads those contents.

It takes exactly one step. Given a chain, it shows only the first hop:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ ln -s current link2
$ ln -s link2 link3

$ readlink link3
link2

link3 contains the text link2. That link2 is itself a link is not readlink's concern.

It also fails on something that is not a link, which makes it a usable test:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ readlink regular_file.txt
$ echo $?
1

No output, non-zero status — Chapter 4.6's idea of a status carrying an answer.

5. realpath — All the Way

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ realpath link3
/home/engineer/pp/releases/v3

realpath resolves everything: every link in the chain, every .., every ., and relative-to-absolute. The result is a fully resolved absolute path to the actual location.

Three separate jobs in one command:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ cd /home/engineer/pp/work

$ realpath current
/home/engineer/pp/releases/v3        # link resolved

$ realpath ../work/./current/rtl
/home/engineer/pp/releases/v3/rtl    # .. and . collapsed too

$ realpath current/../releases
realpath: current/../releases: No such file or directory

That last one is instructive: current/.. resolves physically to releases, so the full path becomes releases/releases, which does not exist. The command is being consistent with §3.

readlink -f does the same job and is the older spelling you will meet in existing scripts:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ readlink -f current
/home/engineer/pp/releases/v3

Use realpath where it is available; recognise readlink -f when you see it. Chapter 1.6's rule applies — both are GNU-flavoured, and a minimal or BSD-derived system may have one, the other, or different options.

CommandAnswers
readlink LINKwhat text is stored in this one link?
realpath PATHwhere does this path actually end up, fully resolved?
readlink -f PATHthe same as realpath, older spelling

6. The Same Pair Across Other Commands

-L and -P recur, and so do their relatives:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ ls -l current        # describes the LINK
$ ls -lL current       # follows it, describes the TARGET

$ cd current           # logical — Bash remembers the route
$ cd -P current        # physical — lands on the real path

$ pwd                  # logical
$ pwd -P               # physical

$ du -sh current       # by default measures the link, not the target
$ du -shL current      # follows it

That last pair is a genuine source of confusion. du reporting a few bytes for something that is plainly a large directory means it measured the link — a tiny text file, per Chapter 6.3 — rather than what it points at.

7. Which One Should You Want?

A rule that resolves most cases:

Use the logical view when you are navigating by hand. It matches how you think, cd .. goes back where you came from, and the path you see is the path you typed. This is the default for exactly that reason.

Use the physical view when the answer feeds a decision. Specifically:

  • before deleting anything — Chapter 3.3's routine, now with links in play. realpath tells you what you are really about to remove.
  • when recording where output went — a logical path containing current means something different next month, after current has been repointed.
  • when comparing two paths — two different logical paths can be the same place, and only the resolved forms can be compared.
  • inside anything saved and re-run — Module 20 treats this as a requirement rather than advice.

The second bullet deserves a line of its own, because it is the one that quietly produces wrong records: writing down work/current/results as the location of a result is writing down where the link pointed at the time. Recording realpath output instead names the actual directory, and still names it after the link moves.

8. The Failures

Assuming pwd is the physical path. It usually is, because most paths contain no links. The moment one does, pwd keeps it.

Expecting cd .. and path/.. to agree. §3. They are resolved by different parties.

Measuring a link and reporting the number. du on a symlink measures a few bytes of text.

Comparing logical paths for equality. /work/current/rtl and /releases/v3/rtl can be the same directory. realpath both before comparing.

Assuming realpath works on something that does not exist. By default it fails if the path does not resolve, which is often what you want and is occasionally not — there are options to relax it, and Chapter 1.6's rule applies.

Following a chain by hand with readlink. It takes one step. For the end of the chain, use realpath.

9. Why This Matters in Engineering Work

The current pattern creates this situation constantly. Chapter 6.3's stable-name-for-a-moving-target is the normal arrangement in shared tool and release areas, which means almost every path you use in such an environment has a link in it and two possible answers.

Recording results. A result recorded as living under current is a record that silently becomes wrong when current is repointed. realpath at the time of writing produces a record that stays true. Part II treats this as part of making a run reproducible.

Knowing which copy of a tool you ran. Chapter 1.7's type tells you which path the shell resolved; realpath on that path tells you which actual program it is after the link farm is unwound. On a machine with several versions installed behind symlinks, that two-command sequence is the complete answer.

Cleaning up safely. Chapter 3.3's routine plus realpath means you know what you are deleting even when the path goes through links — and Chapter 6.3's warning about the trailing slash is exactly this problem.

10. Practice

Use this tree, with work/current a symbolic link to ../releases/v3:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
pp
├── releases
│   └── v3
│       └── rtl
└── work
    └── current -> ../releases/v3

Question 1. After cd pp/work/current, what does pwd print and what does pwd -P print? Why are both correct?

Question 2. From there, where does cd .. land? Where does cd -P .. land? Explain the difference.

Question 3. From pp/work, what does ls current/.. list, and why does it disagree with cd ..?

Question 4. State the rule about who remembers the route and who does not.

Question 5. What does readlink current print? What does realpath current print? Say what each command is for.

Question 6. Given link3 -> link2 -> current -> ../releases/v3, what does readlink link3 give, and what does realpath link3 give?

Question 7. du -sh current reports 14 bytes for what is clearly a large directory. Explain, using Chapter 6.3.

Question 8. You are about to delete something reached through a link. Which command do you run first, and what does it protect you from?

Question 9. You are recording where a job wrote its results, and the path contains current. Why is the logical path a poor record, and what should you store instead?

Question 10. Two paths look completely different. How would you determine whether they are the same directory?

11. Remember

  • Once links exist, a path has two correct answers: the logical route you took, and the physical place the data sits.
  • Bash remembers the route; the kernel does not. That is where the two answers come from.
  • -L is logical, -P is physical, and logical is usually the default.
  • pwd keeps links in the path; pwd -P resolves them.
  • cd .. uses Bash's memory and goes back where you came from. cd -P .. goes to the data's real parent.
  • .. inside a path is resolved by the kernel, physically — so ls current/.. can disagree with cd ... Same two dots, different interpreter.
  • readlink takes one step and prints the stored text. realpath resolves everything — all links, .., ., and relative to absolute. readlink -f is the older spelling of realpath.
  • Logical for navigating by hand; physical whenever the answer feeds a decision — deleting, recording, comparing, or anything saved and re-run.
  • Check how a command treats links before it matters. cp, du, find and tar all differ, and the words to look for are follow and dereference.
  • A recorded path containing a link records where the link pointed at the time. Store the resolved path instead.
  • That is the commonest cause of "it works for me" across two machines — Chapter 22.3 §4 measures pwd and pwd -P disagreeing in both the prefix and the last component.

That completes Module 6, and with it the filesystem. Across five modules you have gone from "there is one tree" to being able to say, for any path on a shared machine, what it names, who may touch it, whether it is one file or one of several names for one file, and where it actually leads once every link is unwound. Module 7 leaves the filesystem behind and turns to what is running — processes, the jobs you start and stop, and the signals that control them.

Where this fits

Part of the Linux & Bash curriculum.