Often I need to search all hidden files and directories within a
particular directory and its sub-directories recursively, and remove
them. On GNU/Linux, the operating system I use, files and directories
beginning with a dot are hidden. They do not appear in the directory
listings by default.
For a long time, I used the find command but I wasn't happy
with the way I used it. Let me demonstrate what I used to do by showing
a sample directory with some hidden directories and the command I used
to run.
susam@nifty:~/lab$ tree
-aF
.
|-- .bar/
| `-- sample/
`-- foo/
`-- .bar/
`-- .bar/
5 directories, 0 files
susam@nifty:~/lab$ find -name ".*" -exec rm -rf {}
\;
rm: cannot remove directory `.'
find: `./foo/.bar': No such file or directory
find: `./.bar': No such file or directory
susam@nifty:~/lab$ tree -aF
.
`-- foo/
1 directory, 0 files
I was not happy with the warnings. There are three of them. The first
one complains that
rm can not remove the current directory.
That's fair.
rm indeed can not remove the current
directory. Try executing
rm -rf . and you'll find the same
warning. But why does it complain about the other two directories? The
directories however are removed as can be seen in the output of the
tree command I executed next.
If this problem appeals to you as a puzzle, you might want to stop here
for a while, think over it and then proceed because you are going to
find spoilers below.
Let us see what is going on and how to fix this command. With the same
setup, this time I won't ask find to actually remove the
directories but instead print the rm command with the
directory names as the argument. This would tell us a little about the
order in which find is trying to remove the directories.
susam@nifty:~/lab$ tree
-aF
.
|-- .bar/
| `-- sample/
`-- foo/
`-- .bar/
`-- .bar/
5 directories, 0 files
susam@nifty:~/lab$ find -name ".*" -exec echo rm
-rf {} \;
rm -rf .
rm -rf ./foo/.bar
rm -rf ./foo/.bar/.bar
rm -rf ./.bar
So, it tries to remove a parent directory first and then its
sub-directories. This explains the other two warnings. It removes
foo/.bar first and then tries to search hidden files within
foo/.bar but this directory no longer exists and hence the
second warning:
find: `./foo/.bar': No such file or
directory. Similarly, it removes
.bar first and then
tries to search within it and so we see the third warning.
find command provides a -depth option to fix
this. Let us try it.
susam@nifty:~/lab$ find -depth
-name ".*" -exec echo rm -rf {} \;
rm -rf ./foo/.bar/.bar
rm -rf ./foo/.bar
rm -rf ./.bar
rm -rf .
But,
rm would still complain that it can not delete the
current directory. We can fix that by refining the pattern.
susam@nifty:~/lab$ find -depth
-name ".?*" -exec echo rm -rf {} \;
rm -rf ./foo/.bar/.bar
rm -rf ./foo/.bar
rm -rf ./.bar
That's perfect. So, here is the proper command in execution.
susam@nifty:~/lab$ find -depth
-name ".?*" -exec rm -rf {} \;
susam@nifty:~/lab$ tree -aF
.
`-- foo/
1 directory, 0 files
No warnings this time.