rm

Delete files and directories, with no undo

Updated 2026-08-28

rm removes a name from a directory. There is no recycle bin on Debian, no undelete and no confirmation, and the file is gone by the time the command returns.

What you may remove is decided by the mode on the directory, not the one on the file. You can delete a read-only file out of a directory you can write, and you cannot delete your own file out of a directory you cannot. /tmp would be unusable under that rule, and the sticky bit on it is the exception that makes it work.

Most of the damage rm does comes from the arguments it was handed rather than the flags it was given: a glob that matched more than you meant, a stray space, an unset variable. rm -rf doesn't report any of it.

Sample files used on this page

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

site the tree the cp and mv pages use as well, including latest.css, which is a symlink to style.css

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.
23 outputs, collapsed by default

Deleting a file

rm unlinks a name. There is no wastebasket on Debian and no undelete, so what follows is written on the assumption that every example here is final.

Delete a file

rm notes.txt && ls notes.txt

No output from rm itself. The ls afterwards is the only evidence you get.

Show output
ls: cannot access 'notes.txt': No such file or directory

Say what was deleted

rm -v site/index.html site/style.css

-v (--verbose) prints a line per file. Worth having on any rm whose arguments came from a glob or a pipeline rather than from your fingers.

Show output
removed 'site/index.html'
removed 'site/style.css'

Delete a file that is not there

rm missing.txt

An error on standard error and an exit status of 1.

Show output
rm: cannot remove 'missing.txt': No such file or directory

Ignore a file that is not there

rm -f missing.txt; echo "exit $?"

-f (--force) suppresses the message and reports success. This is the flag a cleanup step in a script wants, so a second run does not fail on the file the first run removed.

Show output
exit 0

Delete a file whose name begins with a dash

printf x > ./-rf && rm -- -rf && ls -a | grep -c rf

Without -- the shell hands rm a file called -rf and rm reads it as two flags. ./-rf works as well. Both are worth knowing before the day a download lands one in your directory.

Show output
0

Nothing asks, and nothing comes back

Ask before every file

rm -i notes.txt < /dev/null; echo; ls notes.txt

-i (--interactive) prompts per file, and anything other than a yes leaves it alone. Without -i, rm still prompts for a file you cannot write, but only when its input is a terminal; run the same command from a script and it deletes without a word.

Show output
rm: remove regular file 'notes.txt'?
notes.txt

Ask once for a whole batch

rm -I site/index.html site/style.css site/assets/logo.svg site/latest.css < /dev/null; echo; ls site

-I prompts a single time when there are more than three arguments. Three or fewer are deleted without a question, on the reasoning that a short list is one you typed and a long one came from a glob.

Show output
rm: remove 4 arguments?
assets
backups
index.html
latest.css
style.css

An alias is a poor substitute for -I

alias rm="rm -i"; alias rm; unalias rm

Aliasing rm to rm -i teaches you to expect a prompt. The habit follows you onto machines where the alias does not exist, and into scripts, where no alias applies at all. rm -rf overrides the -i anyway.

Show output
alias rm='rm -i'

Directories

rm on its own will not remove a directory

rm site/backups

Even an empty one. The refusal is deliberate: a directory is usually named by accident, as the tail of a glob.

Show output
rm: cannot remove 'site/backups': Is a directory

Remove an empty directory

rmdir site/backups && ls site

rmdir removes nothing that is not empty, which makes it the safe way to clear up a directory you believe you have already emptied. rm -d is the same thing spelled as a flag, and rmdir -p unwinds a whole chain the way mkdir -p builds one.

Show output
assets
index.html
latest.css
style.css

rmdir refuses anything with contents

rmdir site/assets

It will not remove the file inside for you. That refusal is the whole point of using it over rm -r.

Show output
rmdir: failed to remove 'site/assets': Directory not empty

Remove a directory and everything in it

rm -r site/assets && ls site

-r (--recursive) descends and removes as it goes. It asks nothing and reports nothing, so -v is worth adding whenever you are not certain what is down there.

Show output
backups
index.html
latest.css
style.css

rm -rf never complains

rm -rf site; echo "exit $?"; rm -rf site; echo "exit $?"

-r to descend, -f to skip every prompt and every complaint. The first call removed a tree. The second was given a path that no longer exists. Both are silent, and both report success: nothing in the output distinguishes deleting everything from deleting nothing.

Show output
exit 0
exit 0

The permission that decides

Deleting a file is a write to the directory holding it, not to the file. Both consequences surprise people who have only ever read the mode on the file itself.

A read-only file you can still delete

chmod 444 notes.txt && rm -f notes.txt && ls notes.txt

notes.txt is r--r--r-- and it goes anyway, because the entry naming it lives in a directory you can write. See file permissions explained for why the mode on the file has no say.

Show output
ls: cannot access 'notes.txt': No such file or directory

A file you own that you cannot delete

chmod 500 site && rm site/index.html; chmod 755 site

You own index.html and you can write it. site is missing its write bit, so the entry cannot be removed. The chmod at the end puts the directory back.

Show output
rm: cannot remove 'site/index.html': Permission denied

Why /tmp is not a free-for-all

stat -c '%A %n' /tmp

The trailing t is the sticky bit. Everyone can write /tmp, so by the rule above anyone could delete anyone else's files there. The sticky bit restricts removal to the file's owner. Set it with chmod +t.

Show output
drwxrwxrwt /tmp

Ways to remove more than you named

In each of these the command that runs is not the command you meant to type, and rm has no way to tell the difference. The shell has already finished with the arguments by the time rm sees them.

A stray space turns one argument into two

rm notes.txt .txt

The intention was notes.txt and a typo produced two arguments. notes.txt is gone. The only complaint is about the argument that was never a file. Now imagine the first one had been *.

Show output
rm: cannot remove '.txt': No such file or directory

An unset variable expands to nothing

unset DIR; rm -rf "$DIR"/

"$DIR"/ becomes / on its own, and only the --preserve-root failsafe stands between that and the whole machine. Quoting does not save you here; set -u in a script does, by refusing to expand an unset variable at all.

Show output
rm: it is dangerous to operate recursively on '/'
rm: use --no-preserve-root to override this failsafe

A trailing slash on a symlink empties the target

ln -s site sitelink && rm -r sitelink/; ls -A site | wc -l

rm follows the slash into site, deletes everything it finds and then reports that sitelink/ is not a directory. Both the link and the now-empty directory survive. The exit status is 1, so a script that checks only that will carry on.

Show output
rm: cannot remove 'sitelink/': Not a directory
0

Without the slash, only the link goes

ln -s site sitelink && rm sitelink && ls -A site | wc -l

rm never follows a symlink it was asked to remove, so removing a link made by ln -s leaves the target alone. All five entries in site are still there. Tab completion adds that slash for you on a link to a directory, so the safe form is the one you have to delete a character to reach.

Show output
5

Deleting many files at once

A glob is expanded by the shell, so a directory with a hundred thousand files in it can produce a command line too long to run. find is the way round that, and it also lets you look before you delete.

List what you are about to delete

mkdir -p logs && touch logs/app.log.1 logs/app.log.2 logs/keep.log && find logs -name "*.log.[0-9]" -print | sort

The same expression that will do the deleting, with -print in place of -delete. sort is needed because find returns entries in directory order, which is neither alphabetical nor stable.

Show output
logs/app.log.1
logs/app.log.2

Then delete exactly those

mkdir -p logs && touch logs/app.log.1 logs/app.log.2 logs/keep.log && find logs -name "*.log.[0-9]" -delete && ls logs

-delete is built into find, so no second command runs and no filename is ever re-parsed by a shell. keep.log did not match and is untouched.

Show output
keep.log

Or hand them to rm safely

mkdir -p logs && touch logs/app.log.1 logs/app.log.2 logs/keep.log && find logs -name "*.log.[0-9]" -print0 | sort -z | xargs -0 rm -v

-print0 and -0 separate names with a null byte, which is the one character a filename cannot contain. Use this when you need a flag -delete cannot give you; see xargs for what goes wrong without the null.

Show output
removed 'logs/app.log.1'
removed 'logs/app.log.2'