By default (at least in Ubuntu 9.04) if you use the
.*
wildcard in Bash, it will match the
.
and
..
entries as well. I've seen not just once users executing this in a directory:
rm -r * .*
Their intention was to remove all entries in the current directory. However since the default shell is bash and by default the
.*
matches
..
too,
the command will traverse up in the directory tree ... at least as far as it can go with the user's privileges.
A simple (precautionary) workaround for mistakes like the one above is to set the GLOBIGNORE
variable to some meaningful value in the /etc/bash.bashrc
initialization file. Eg.
GLOBIGNORE=.:..
This will automatically turn on dotglobbing (thus the *
wildcard will now match names starting with a dot too), but it will prevent disastrous mistakes. You get some and you loose some.
Update: as somebody (anonymously) pointed out in a comment ... the
rm -r .*
will _not_ traverse up in the directory tree. At least not on an Ubuntu 9.04 setup. Instead you'll get the following error message:
rm: cannot remove directory `.'
rm: cannot remove directory `..'
Since the commenter did not elaborate on the matter, I did my own digging. Going through the source of the
rm
command (which you can find in the
coreutils
package) it became clear that it'll eventually make a call to the
unlinkat()
function which (as described in it's manpage)
"operates in exactly the same way as either unlink(2) or rmdir(2) (depending on whether or not flags includes the AT_REMOVEDIR flag) except ...". Now looking at the manpage of the
rmdir()
function (not the
rmdir
utility in man section 1, but the function in man section 2!) it says:
"rmdir() deletes a directory, which must be empty. On success, zero is returned. On error, -1 is returned, and errno is set appropriately".
Among the various error messages you find these:
- EINVAL: pathname has "." as last component
- ENOTEMPTY: pathname contains entries other than "." and ".." ; or, pathname has ".." as its final component. POSIX.1-2001 also allows EEXIST for condition
So now it's clear: Bash does match "." and ".." via the ".*" wildcard and
/bin/rm
gets executed with the
rm . ..
commandline after the expansion. But
rm
itself prevents the recursive deletion of "." or "..".
I can clearly remember that I've already used a system where
rm
did nothing the like and allowed recursive deletes _up_ the directory tree via the "." and ".." entries in a directory. Please, correct me if I'm wrong ... was it Solaris 2.5 or maybe Solaris 7?
I must admit that I haven't tested my original hypothesis (that an
rm -r .*
would go up and delete the parent directory, etc.) ... I jumped to conclusions that I shouldn't have. Of course the anonymous commenter could have explained it a bit more in detail, but her/his simple "wrong" was enough to get me to verify the facts. Thanks for that.
PS: in
zsh
the
.*
wildcard does not match the
.
and
..
entries by default. A lot more saner approach imho.
Comments
However since the default
Wrong.
Re: However since the default