GLOBIGNORE - a useful bash default for all users

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. Smile

PS: in zsh the .* wildcard does not match the . and .. entries by default. A lot more saner approach imho.

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

However since the default

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.


Wrong.

Re: However since the default

Thanks. This time I've done my homework thoroughly and corrected the original post.