Learn › Linux Foundations › Manipulation

Chapter 6, Part 2. Manipulation: ln, ln -s, readlink, basename, dirname and realpath

Final test: Chapter 6, Manipulation. Twenty-five objectives on a real Debian 12 server covering both parts: touch, mkdir, rmdir, rm, cp and mv, then ln, ln -s, readlink, basename, dirname and realpath, one working directory per objective. Fifty minutes, graded from the state of the directories and from the commands you run.

Chapter 6, Part 2. Manipulation

This chapter covers the following topics:

This is Part 2 of 2. Part 1 covered Sections 6.1 to 6.9, the tools that create, copy, move and delete. This part covers Sections 6.10 to 6.15: two kinds of link, the tool that reads a link, and three tools that work on a path as a string or resolve it. It ends in the chapter lab, which covers all fifteen sections.

What you already know: Part 1 worked on files and directories, and every operation left exactly one name for each thing. This part separates names from files: a file can have two names, and a name can point at a path instead of a file.

Objectives

After this part you can:

Before you start a machine, each exercise shows a rehearsal terminal. It checks the requested command against an example and does not change Linux files. Start one real practice machine and use its admin@server1 shell across these steps. Commands there can change real files; a wrong command can still have an effect even if the lesson does not mark the exercise complete. The restock helper rebuilds a named exercise bay when you need to try again.

The final Test Room starts a separate Debian 12 server with one bay per objective across both parts and checks its objectives automatically as you work.

Pre-check 1 of 5: two names

Five questions before the material. A wrong answer costs nothing: the explanation states the fact and names the section that teaches it.

Pre-check 2 of 5: a link to nothing

Pre-check 3 of 5: what a link stores

Pre-check 4 of 5: the two halves of a path

Pre-check 5 of 5: dots in a path

6.10 A second name for a file: ln

Chapter 2 explained that a file has an inode, an ID number under which the system keeps its contents and metadata, and that a name in a directory is a pointer to an inode. Nothing stops two names from pointing at the same inode. Such a second name is a hard link, and the count of names an inode has is its link count, the number in the second column of ls -l.

You are in your home directory. The fourteenth bay, staging/bay-14, holds a report. Its link count is 1: one name, one file. List it in long form:

prompt: admin@server1:~$ answer: ls -l staging/bay-14 ||| ls -l staging/bay-14/ ||| ls -l ~/staging/bay-14 output: total 4 -rw-r--r-- 1 admin admin 70 Jan 15 2026 report-2026-08.txt hint: Section 2.4. The listing command with its long option -l, then the path staging/bay-14 (or ~/staging/bay-14 from anywhere).

One file of 70 bytes. The 1 after the permissions is the link count: exactly one name refers to this file. Every regular file you have listed so far had a 1 there.

6.10 ln makes the second name

ln, link, takes the existing name first and the new name second, and creates the new name as a second pointer to the same inode. Nothing is copied. You are in your home directory. Give staging/bay-14/report-2026-08.txt a second name, staging/bay-14/report-current.txt:

prompt: admin@server1:~$ answer: ln staging/bay-14/report-2026-08.txt staging/bay-14/report-current.txt ||| ln ~/staging/bay-14/report-2026-08.txt ~/staging/bay-14/report-current.txt output: hint: Section 6.10. The link command, then the existing name staging/bay-14/report-2026-08.txt, then the new name staging/bay-14/report-current.txt (both can be written ~/staging/... from anywhere).

Silent, like every tool in this chapter. The order is the one cp and mv use: what exists, then what to create. Reversing it makes ln refuse, because the source it was given does not exist.

6.10 Two names, one file

You are in your home directory. List staging/bay-14 in long form again:

prompt: admin@server1:~$ answer: ls -l staging/bay-14 ||| ls -l staging/bay-14/ ||| ls -l ~/staging/bay-14 output: total 8 -rw-r--r-- 2 admin admin 70 Jan 15 2026 report-2026-08.txt -rw-r--r-- 2 admin admin 70 Jan 15 2026 report-current.txt hint: Section 2.4. The long listing -l of staging/bay-14 again (or ~/staging/bay-14 from anywhere).

Two entries, and the link count of both reads 2. The size, permissions and displayed January modification time match because both names reach one file. ln increased its link count but did not copy or rewrite its bytes. Compare Exercise 6-25 in Part 1: a copy carried a new timestamp because a second file was written.

6.10 The same bytes through either name

You are in your home directory. Print the report through its new name, staging/bay-14/report-current.txt:

prompt: admin@server1:~$ answer: cat staging/bay-14/report-current.txt ||| cat ~/staging/bay-14/report-current.txt output: Staging report Pages checked: 3 Broken links: 0 Ready to release: yes hint: Section 5.1. The printing command from Chapter 5, then the path staging/bay-14/report-current.txt (or ~/staging/bay-14/report-current.txt from anywhere).

The report, through the name that did not exist a minute ago. A change written through either name appears under the other, because both open the same inode. This is what makes a hard link different from a copy.

6.10 Linking into a directory

The new name can be in another directory on the same filesystem. You are in your home directory. The fifteenth bay, staging/bay-15, holds a CSV export and an empty exports directory. Give staging/bay-15/data.csv a second name inside staging/bay-15/exports:

prompt: admin@server1:~$ answer: ln staging/bay-15/data.csv staging/bay-15/exports/data.csv ||| ln ~/staging/bay-15/data.csv ~/staging/bay-15/exports/data.csv ||| ln staging/bay-15/data.csv staging/bay-15/exports/ ||| ln staging/bay-15/data.csv staging/bay-15/exports output: hint: Section 6.10. The link command, then the existing name staging/bay-15/data.csv, then the new name staging/bay-15/exports/data.csv (both can be written ~/staging/... from anywhere).

Silent. As with cp, naming an existing directory as the destination would also have worked and kept the file name. A hard link cannot cross to another filesystem, because an inode number means something only within its own filesystem. ln cannot hard-link a directory at all; the step Errors you will meet shows that refusal.

6.10 The link count in the other directory

You are in your home directory. List staging/bay-15/exports in long form:

prompt: admin@server1:~$ answer: ls -l staging/bay-15/exports ||| ls -l staging/bay-15/exports/ ||| ls -l ~/staging/bay-15/exports output: total 4 -rw-r--r-- 2 admin admin 84 Jan 15 2026 data.csv hint: Section 2.4. The long listing -l, then the path staging/bay-15/exports (or ~/staging/bay-15/exports from anywhere).

Link count 2, seen from the other directory. The listing of a directory shows the names in it; the count tells you how many names the file has in total, wherever they are.

6.10 Removing one name

You are in your home directory. The scratch directory, staging/scratch, holds a data file. Give staging/scratch/data.txt a second name, staging/scratch/data-link.txt, then remove the first, and watch what survives:

prompt: admin@server1:~$ answer: ln staging/scratch/data.txt staging/scratch/data-link.txt ||| ln ~/staging/scratch/data.txt ~/staging/scratch/data-link.txt output: hint: Section 6.10. The link command, then staging/scratch/data.txt, then the new name staging/scratch/data-link.txt (both can be written ~/staging/... from anywhere).

Silent. Two names now point at the data.

6.10 rm removes a name, not the file

You are in your home directory. Remove the first name, staging/scratch/data.txt:

prompt: admin@server1:~$ answer: rm staging/scratch/data.txt ||| rm ~/staging/scratch/data.txt output: hint: Section 6.5. The removal command from Part 1, then the path staging/scratch/data.txt (or ~/staging/scratch/data.txt from anywhere). The new name stays.

Silent. rm removed one name and lowered the link count by one. The system frees the contents of a file only when its link count reaches zero, and it is not zero.

6.10 The file survives

You are in your home directory. List staging/scratch in long form:

prompt: admin@server1:~$ answer: ls -l staging/scratch ||| ls -l staging/scratch/ ||| ls -l ~/staging/scratch output: total 4 -rw-r--r-- 1 admin admin 80 Jan 15 2026 data-link.txt hint: Section 2.4. The long listing -l, then the path staging/scratch (or ~/staging/scratch from anywhere).

One name left, link count back to 1, and the January timestamp: the file was never touched. Which name was created first makes no difference; there is no original and no copy, only names.

6.10 Read through the surviving name

You are in your home directory. Print the surviving name, staging/scratch/data-link.txt:

prompt: admin@server1:~$ answer: cat staging/scratch/data-link.txt ||| cat ~/staging/scratch/data-link.txt output: Scratch data for the hard link demonstration. Two names can share this content. hint: Section 5.1. The printing command, then the path staging/scratch/data-link.txt (or ~/staging/scratch/data-link.txt from anywhere).

The contents are intact. This is how archive/2025/annual-report.txt and archive/2025/annual-report-final.txt from Chapter 2 hold the same text: they are two names for one file.

6.11 A pointer to a path: ln -s

A symbolic link, also called a symlink or a soft link, is a different thing: a small file whose contents are a path. When you open the link, the system reads the path stored in it and opens whatever is there instead. The link stores the path as text, exactly as you wrote it. It can point at a directory, at a file on another filesystem, or at a path where nothing exists yet.

{ "height": 210, "caption": "A hard link is a second name for the same file. A symbolic link is a file that stores a path. Click each node.", "nodes": [ { "id": "n1", "label": "report-2026-08.txt", "x": 0, "y": 20, "detail": "A name in bay-14. It points at an inode, the ID number under which the system keeps the contents." }, { "id": "n2", "label": "report-current.txt", "x": 0, "y": 120, "detail": "The hard link: a second name pointing at the same inode. Link count 2. Neither name is the original." }, { "id": "f", "label": "one inode, 70 bytes", "x": 210, "y": 70, "detail": "The file itself: contents plus metadata. It exists as long as at least one name points at it." }, { "id": "s", "label": "latest", "x": 420, "y": 20, "detail": "A symbolic link in bay-16. Its own contents are the 18 characters release-2026-08-01. ls -l shows it with type l and an arrow." }, { "id": "t", "label": "release-2026-08-01", "x": 420, "y": 120, "detail": "The target path, resolved from the directory the link sits in. If nothing is there, the link dangles." } ], "edges": [ { "from": "n1", "to": "f", "label": "name" }, { "from": "n2", "to": "f", "label": "name" }, { "from": "s", "to": "t", "label": "stores the path" } ] }

You are in your home directory. The sixteenth bay, staging/bay-16, holds two dated release directories, the shape the website directory from Chapter 2 uses. List it:

prompt: admin@server1:~$ answer: ls staging/bay-16 ||| ls staging/bay-16/ ||| ls ~/staging/bay-16 output: release-2026-07-15 release-2026-08-01 hint: Section 2.2. The plain listing command, then staging/bay-16 (or ~/staging/bay-16 from anywhere).

Two releases, the newer one on the right. A name that always means the newest release, whichever directory that is, is what a symbolic link is for; website/current in Chapter 2 is exactly that.

6.11 ln -s with a relative target

The -s option, symbolic, makes ln create a symbolic link. The order is the same: the target first, then the link. The target is stored as written, and a relative target is resolved from the directory the link sits in, not from where you stand when you create it. So a link inside bay-16 that should point at a directory beside it stores just that directory name. You are in your home directory. Create the link staging/bay-16/latest with the target release-2026-08-01:

prompt: admin@server1:~$ answer: ln -s release-2026-08-01 staging/bay-16/latest ||| ln -s release-2026-08-01 ~/staging/bay-16/latest output: hint: Section 6.11. The link command with its symbolic option -s, then the target release-2026-08-01 exactly as the link should store it, then the link path staging/bay-16/latest (or ~/staging/bay-16/latest from anywhere; the target is stored as text either way).

Silent. ln did not check that release-2026-08-01 exists, because the target is only text to it. The name is correct here because the link sits in the same directory as the release. Writing staging/bay-16/release-2026-08-01 as the target would have stored that longer path, and it would not resolve from inside bay-16; Exercise 6-59 shows that mistake.

6.11 The link in a listing

You are in your home directory. List staging/bay-16 in long form:

prompt: admin@server1:~$ answer: ls -l staging/bay-16 ||| ls -l staging/bay-16/ ||| ls -l ~/staging/bay-16 output: total 8 lrwxrwxrwx 1 admin admin 18 Sep 6 14:44 latest -> release-2026-08-01 drwxr-xr-x 2 admin admin 4096 Jan 15 2026 release-2026-07-15 drwxr-xr-x 2 admin admin 4096 Jan 15 2026 release-2026-08-01 hint: Section 2.4. The long listing -l, then the path staging/bay-16 (or ~/staging/bay-16 from anywhere).

The first character of the link line is l, the type of a symbolic link, where a directory shows d and a file -. Its size is 18, the length of the text release-2026-08-01, because that text is what the link holds. The arrow shows the stored target. The timestamp is the moment you created the link, so yours differs. The permissions of a link are always shown as rwxrwxrwx and never matter; the target decides who may do what.

6.11 Through the link

You are in your home directory. Use the link as if it were the directory it points at: list staging/bay-16/latest/, with the slash:

prompt: admin@server1:~$ answer: ls staging/bay-16/latest/ ||| ls staging/bay-16/latest ||| ls ~/staging/bay-16/latest/ output: VERSION index.html hint: Section 2.2. The plain listing command, then the path staging/bay-16/latest/ with the slash at the end (or ~/staging/bay-16/latest/ from anywhere).

The contents of release-2026-08-01, reached through the name latest. The slash after the link name tells ls to list what the link points at rather than the link itself. Any program given the path staging/bay-16/latest/index.html opens the file in the release directory, and when the next release is deployed, pointing latest at it changes every such path at once.

6.11 An absolute target

An absolute target starts with a slash and resolves from the root, so it works wherever the link sits. You are in your home directory. The seventeenth bay, staging/bay-17, is empty; create the link staging/bay-17/os-release with the target /etc/os-release, the file that names the operating system, which Chapter 4 read:

prompt: admin@server1:~$ answer: ln -s /etc/os-release staging/bay-17/os-release ||| ln -s /etc/os-release ~/staging/bay-17/os-release output: hint: Section 6.11. The symbolic option -s, then the absolute target /etc/os-release, then the link path staging/bay-17/os-release (or ~/staging/bay-17/os-release from anywhere).

Silent. The link crosses from your home directory into /etc, which a hard link could not do if /etc were on another filesystem. It also points at a file you do not own, which a symbolic link is free to do.

6.11 The absolute arrow

You are in your home directory. List staging/bay-17 in long form:

prompt: admin@server1:~$ answer: ls -l staging/bay-17 ||| ls -l staging/bay-17/ ||| ls -l ~/staging/bay-17 output: total 0 lrwxrwxrwx 1 admin admin 15 Sep 6 14:44 os-release -> /etc/os-release hint: Section 2.4. The long listing -l, then the path staging/bay-17 (or ~/staging/bay-17 from any

Practice Chapter 6, Part 2. Manipulation: ln, ln -s, readlink, basename, dirname and realpath in a real Linux terminal at The Linux Camp. Progress is verified automatically as you type commands on the machine.

More lessons in Manipulation