3
rm -fr *

won't delete .files

On the other hand,

rm -fr * .*

will delete too much!

Is there a reliable way to recursively delete all contents of a directory in Bash?

One way I can think of is:

rm -fr $PWD
mkdir $PWD
cd $PWD

This has the side-effect of deleting $PWD temporarily.

Cyrus
  • 84,225
  • 14
  • 89
  • 153
MWB
  • 11,740
  • 6
  • 46
  • 91

4 Answers4

6

I suggest to use first:

shopt -s dotglob

dotglob: If set, bash includes filenames beginning with a . in the results of pathname expansion

Cyrus
  • 84,225
  • 14
  • 89
  • 153
1
rm -fr * .*

is relatively "safe". rm is forbidden by POSIX from acting on . and ...

rm -rf . .. 

will be a no-op, though it will return 1. If you don't want the error return, you can do:

rm -rf .[!.]* 

which is POSIX standardized, no bash extension required.

You can also use find:

find . -delete 
Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
1

You could use find with -delete and -maxdepth:

find . -name "*" -delete -maxdepth 2

So let's say you are in the directory temp which looks like this:

./temp
     |_____dir1
     |        |_____subdir1
    X|_file  X|_file      |_file
     |
    X|_____dir2
             X|_file

Looking at the tree the files and directories which have an X next to them would be deleted using the command above. subdir1 is spared, since the maximum depth at which find will delete a file is set at 2 and there is a file residing within it. find will delete files starting with . — however, it doesn't work for symbolic links.

 -delete
         Delete found files and/or directories.  Always returns true.
         This executes from the current working directory as find recurses
         down the tree. It will not attempt to delete a filename with a
         ``/'' character in its pathname relative to ``.'' for security
         reasons. Depth-first traversal processing is implied by this
         option. Following symlinks is incompatible with this option.
l'L'l
  • 44,951
  • 10
  • 95
  • 146
1

The usual wisdom for UNIX is to use something like:

rm -rf * .[!.]* ..?*

That will list all files that start with a dot or even double dot (without including the plain double dot (./..).

But that globbing expansion will keep the asterisk if files of that type do not exist.

Let's test:

$ mkdir temp5; cd temp5
$ touch {,.,..}{aa,bb,cc}
$ echo $(find .)
. ./aa ./cc ./..bb ./..aa ./.cc ./.bb ./..cc ./.aa ./bb

And, as indicated, this will include all files:

$ echo * .[!.]* ..?*
aa bb cc .aa .bb .cc ..aa ..bb ..cc

But if one of the types doesn't exist, the asterisk will stay:

$ rm ..?*
$ echo * .[!.]* ..?*
aa bb cc .aa .bb .cc ..?*

We need to avoid arguments that contain an asterisk to workaround this issue.