Skip to content
academia.sh

Lesson 08 / 20

Deletion and Irreversibility

Defining deletion as unlinking, the effect of open file descriptors, verification habits for destructive commands, and the danger of filenames starting with a dash.

Contents

The previous lesson’s commands could lose data too, but all of them did it by overwriting something. Deletion destroys directly, and the file system has no “undo” operation: a file deleted from the command line does not go to a trash can.

This lesson first defines what deletion is at the kernel level — the definition explains why a deleted file can sometimes still be read — and then establishes the verification habits to apply with destructive commands.

Deleting Is Unlinking

What the rm command does is narrower than its name suggests: it removes the directory entry. The name of this operation at the system call level is unlink.

Every file’s metadata keeps a link count: the number of directory entries pointing to the file. rm decreases this count by one. The data blocks are freed only when both conditions are met:

  1. The link count has dropped to zero.
  2. No process is left holding the file open.

The second condition can be shown directly:

$ printf 'temporary data\n' > /tmp/temp.txt
$ exec 3< /tmp/temp.txt
$ rm /tmp/temp.txt
$ ls -l /tmp/temp.txt
ls: cannot access '/tmp/temp.txt': No such file or directory
$ cat <&3
temporary data
$ exec 3<&-

The file has been deleted: its name is not in the directory, and listing gives an error. Yet its content could still be read, because a file descriptor is holding it open. The moment the descriptor closes, the data actually becomes free.

This behavior has two practical consequences. First, when a running program’s log file is deleted, the program keeps writing and the disk space is not reclaimed; the space is freed only when the program restarts. Second, deleting a file a program is using does not crash it — the program uses the file by its descriptor, not by its name.

If the link count is greater than one, deletion does not touch the data at all; only one of the names goes. How this is possible is the subject of the next lesson.

Deleting Directories

Directories are deleted with two commands, and the difference between them is a safety criterion.

rmdir deletes only empty directories:

$ ls archive
raw-p  raw-backup
$ rmdir archive/raw-p
rmdir: failed to remove 'archive/raw-p': Directory not empty

rm -r deletes a directory together with its contents, recursively:

$ rm -r archive/raw-p
$ ls archive
raw-backup

This removes the second backup that was created in the previous lesson to demonstrate the effect of the -p option; the raw-backup directory continues to exist.

rmdir failing is not a flaw, it is a feature: when it reports that the directory is not empty, it announces the presence of content that was not intended to be deleted. Using rmdir to verify that a directory has been emptied is safer than using rm -r.

What -f Does and Does Not Do

rm gives an error for a nonexistent file and returns a non-zero status:

$ rm /tmp/demo2/missing.txt
rm: cannot remove '/tmp/demo2/missing.txt': No such file or directory
$ echo $?
1
$ rm -f /tmp/demo2/missing.txt
$ echo $?
0

The -f option does two things: it gives no error for nonexistent files, and it does not ask for confirmation. It does not increase the power to delete — it does not delete a file that cannot be deleted; permissions still apply.

-f is used often in scripts because the “fine if it is already gone” behavior is needed. But because it also turns off confirmation questions, combined with -r it becomes the most dangerous form.

Verification Habits

A fact established in the shell lesson turns into a safety tool here: expansion is done by the shell, the command only sees the result. So the result can be seen before the command is run.

$ echo data/raw/*.csv
data/raw/measurement-01.csv data/raw/measurement-02.csv data/raw/measurement-03.csv
$ echo rm data/raw/*.csv
rm data/raw/measurement-01.csv data/raw/measurement-02.csv data/raw/measurement-03.csv

The second line is the exact form of the command that will run. Putting echo before a deletion command and reading the output lets you see a misspelled pattern before paying its price. Running a destructive command this way once is a one-step check that should become a habit.

Testing the pattern with ls does the same job and also verifies that the files actually exist.

The shell’s word-splitting rule becomes critical here. If rm data/raw/*.csv is mistakenly written as rm data/raw/* .csv — a space between the star and the dot — the pattern matches every file and the directory is emptied. The difference between the two commands is a single space; the echo pre-check shows this difference before it produces a result.

rm offers two confirmation options for interactive use:

$ rm -i normal.txt
rm: remove regular empty file 'normal.txt'? n
$ rm -I *.txt
rm: remove 5 arguments? n

-i asks separately for every file and is defined in POSIX. -I asks once, only when more than three files are given or -r is used; it is specific to GNU tools and is not found in BSD tools. -I is more usable in practice: being asked about every single file trains the user to answer “yes” automatically, while a single question is actually read.

Names Starting with a Dash

Why the -- separator introduced in the shell lesson is necessary becomes visible here:

$ ls
-log.txt  normal.txt
$ rm -log.txt
rm: invalid option -- 'l'
Try 'rm ./-log.txt' to remove the file '-log.txt'.
Try 'rm --help' for more information.
$ rm -- -log.txt
$ ls
normal.txt

The command has taken the argument starting with a dash for an option. Everything after the -- separator is always interpreted as a filename; the ./ prefix does the same job, because the name no longer starts with a dash.

This is not a theoretical problem. A pattern matching such a name can cause a command to run with unexpected options. In scripts, filenames coming from the user should always be given after a -- separator.

The Most Destructive Form

The rm -rf combination deletes everything under the given path, without asking and without reporting errors. Run on the root directory, it targets the entire system.

This command has not been run in this lesson and must not be tried. Common tool implementations have a guard for the root directory and the command is refused, but the guard can be disabled with an option and is not present on every system. The real danger is not even the root itself: with an empty variable, a line like rm -rf "$dir"/ targets the root directory.

In practice, three habits prevent most mistakes of this class.

Working with a relative path inside the directory instead of an absolute path. In the wrong directory, the command either gives an error or affects only a narrow area.

Running echo before rm on lines with variable paths. If the variable is empty, this shows up immediately in the output.

Moving instead of deleting. Moving content aside before deleting it keeps the decision reversible; the moved directory is deleted deliberately after a while.

The only reliable system-level way to recover deleted data is a backup. Backup and snapshot strategies are the subject of the System Administration course; there is no undo within the scope of this course.

Summary

  • Deletion removes the directory entry and decreases the link count; data is not freed until the count reaches zero and no process still holds the file open.
  • rmdir deletes only empty directories, and this failure is a safety criterion; rm -r deletes it together with its contents.
  • -f turns off confirmation and the error for a nonexistent file; it does not grant the authority to delete.
  • Putting echo before a destructive command shows the argument list the shell will produce, without producing the result.
  • -i asks once per file, -I asks once for a batch operation; -I is not portable.
  • Filenames starting with a dash are mistaken for options; the -- separator or the ./ prefix removes this.

Next Step

The definition of deletion left a question open: how can the link count be greater than one, that is, how can a file have more than one name? The next lesson answers this question and compares two different kinds of link — the hard link, which shares the same inode, and the symbolic link, which holds a path string.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ Esc navigate · open · close