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
  • 77,979
  • 13
  • 71
  • 125
MWB
  • 10,855
  • 6
  • 41
  • 80

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
  • 77,979
  • 13
  • 71
  • 125
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 
PSkocik
  • 55,062
  • 6
  • 86
  • 132
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
  • 42,597
  • 8
  • 87
  • 135
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.

done
  • 6,370
  • 27
  • 49