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.

No comments

Post a comment

RSS