60

The expression .* is expanded by bash to include the current and parent directories:

$ ls -la
total 2600
drwxrwxrwx   2 terdon terdon 2162688 Sep 10 16:22 .
drwxr-xr-x 142 terdon terdon  491520 Sep 10 15:34 ..
-rw-r--r--   1 terdon terdon       0 Sep 10 16:22 foo
$ echo .*
. ..

If I run rm -rf .* on my Debian using GNU bash, version 4.2.36(1)-release and rm from rm (GNU coreutils) 8.13, I get this message:

$ rm -rf .*
rm: cannot remove directory: `.'
rm: cannot remove directory: `..'

Is this a GNU thing or is it POSIX? Are there any *nix systems where the command above will silently delete . and ..?

Also, is this a safety feature of the shell or of the rm command itself?

1
  • 7
    I know this question is in the context of rm, but I thought it was worth mentioning that you can still have unexpected results with chmod,chown,etc when matching .*. Sep 10, 2013 at 17:46

1 Answer 1

66

The latest (as of 2017) version of the POSIX spec for the rm utility is here (and the previous one there) and forbids the deletion of . and ...

If either of the files dot or dot-dot are specified as the basename portion of an operand (that is, the final pathname component) or if an operand resolves to the root directory, rm shall write a diagnostic message to standard error and do nothing more with such operands.

As noted by @jlliagre, the part about / is an addition in SUSv4.

The oldest publicly available Unix spec that I could find (XPF4 CAE rev2 (1994)), already specified that . and .. can't be removed, though comments in the GNU fileutils changelog suggest it was already the case in older POSIX specs.

Note that it applies to dir/.. and ../ as well, but some implementations (including UNIX-certified ones like Solaris 11 and macOS) still don't safeguard against rm -rf ../ or rm -rf .*/).

history

Early unices

The -r option to rm was added in Unix V3 (1973) though it was only deleting the content of the directories, you'd still need to use rmdir to remove directories.

That changed in Unix V7 (1979, the release that also introduced the Bourne shell and from which most Unices derive). rm -r now removed directories as well and would not delete the .. directory tree. The man page states:

It is forbidden to remove the file .. merely to avoid the antisocial consequences of inadvertently doing something like rm -r .*.

(though one might argue that rm -r .* is still antisocial as it deletes everything because . is included).

It still did accept to remove . though it would not unlink the . or .. entries. So then, rm -r . was an effective way to empty the current directory.

Also note that the safeguard was only for a literal .. argument, not for dir/.. or ./... So, rm -rf ./.* would still remove everything in the parent directory recursively.

It's interesting to see that that was already to workaround the bug/misfeature by which globs could include . and .. in their expansion. That was fixed in the Forsyth shell (the basis for the original Minix shell and pdksh) in the late 80s, zsh (1990) and fish (2005) but not other shells and in particular not the POSIX sh language that requires the expansion of .* to include . and .. if they are returned by readdir() (bash addresses the problem partly only with shopt -s dotglob where globs (except the .xxx ones) don't include . or .., and with ksh, you can fix it by doing FIGNORE='@(.|..)').

When exactly forbidding . as well was added is not always clear and varies with each Unix. A few findings below.

BSDs

The forbidding of . was added sometime between 2.9BSD (1983) and 2.10BSD (1987) and between 4.2BSD (1983) and 4.3BSD (1986) (see this change timestamped 1985 in the unix-history-repo).

$ wget -qO- http://www.tuhs.org/Archive/PDP-11/Distributions/ucb/2.9BSD/root.tar.gz |
    zgrep -ao 'rm: canno[[:print:]]*'
rm: cannot remove `..'
$ wget -qO- http://www.tuhs.org/Archive/PDP-11/Distributions/ucb/2.10bsd.tar.gz |
    zgrep -ao 'rm: canno[[:print:]]*'
rm: cannot remove `.' or `..'
rm: cannot remove `.' or `..'\n");

For dir/. and dir/.., see this change in 1988 (BSD 4.3 Net/1).

To this date, the rm of FreeBSD (and derivatives like macOS) still empties the current or parent directory upon rm -rf ./ or rm -rf ../ though (matters for rm -rf .*/).

System V

I don't have much information as neither source or binary are publicly available for the AT&T Unix derivatives after V7. In its online manual, HPUX (based on System III) still mentions that it only forbids .. while effectively it forbids both which is an indication that probably at least SysIII didn't forbid deletion of . (edit: Now looking at the SysIII rm source code, it's virtually unchanged since Unix V7).

All other online manuals I've checked mention deleting . or .. is forbidden which is to be expected to be POSIX conformant.

Solaris rm still empties the current or parent directory upon rm -rf ./ or rm -rf ../.

GNU

The early changelog for the GNU fileutils has all the historical information.

While originally neither deleting . or .. were forbidden, .. was forbidden first and then both (including dir/.), all between 1990 and 1991.

other

As we saw, in zsh, the expansion of .* (or any glob) never includes . or .. (even in sh emulation mode). The rm builtin (which you get if you zmodload zsh/files) therefore does not treat . or .. specially. So, with that zsh builtin, you can rm -rf . or rm -rf .. to empty . or .., but rm -rf .* will not remove . or ...

In busybox rm, the forbidding of deletion of . and .. was added in 0.52 (2001)

6
  • Odd, that seems to specify that rm -rf . / (note the space) should print two warnings (for . and /) and exit, but we seem to get a question asking how to recover from that every couple months.
    – Kevin
    Sep 10, 2013 at 16:00
  • 6
    @Kevin Not all systems are POSIX compliant and the root directory restriction was only explicitly added in the latest POSIX release.
    – jlliagre
    Sep 10, 2013 at 16:15
  • @jlliagre I see. GNU generally tries to implement POSIX (+extensions, of course), and I'd imagine they'd want to put this one in, but if it's fairly new that would explain it.
    – Kevin
    Sep 10, 2013 at 16:23
  • 2
    @Stephane: you're right, but I'd still add a big "Yes, it could happen! But ..." at the beginning of your answer, so that people undoubtedly know that indeed, on some (older or just non-POSIX compliant) systems, they could delete parent directories. I try to always point out those possibility (ie, I try to stay on the safe side, even if it makes the answer sometimes harder to read/remember) ^^ Sep 10, 2013 at 17:49
  • 1
    @MartinSchröder, On BSDs, it was added somewhere between 2.8BSD and 2.10BSD (before only ".." was forbidden like in UnixV7) and between 3BSD and 4.3RENO. On SysV systems, it's less clear. HPUX manual for instance claims it only forbid ".." but in effect it forbids both "." and "..", it's only the manual that is not up to date. Sep 11, 2013 at 12:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .