Delete all files except one
A couple of days I was asked if knew an easy way to delete all but one files in a directory. If you didn't already guess it from this blog entry's title, there is a simple way - or to be more precise - there are several ways. The first one is quiet straightforward and uses the find command:
find . -not -name do_not_delete_me -delete
This works recursively and also preserves files named do_not_delete_me contained in sub-folders of the current directory:
user@host /tmp/test $ ls -R .: a b c do_not_delete_me foo ./a: foo ./b: bar do_not_delete_me ./c: baz user@host /tmp/test $ find . -not -name do_not_delete_me -delete find: cannot delete `./b': Directory not empty user@host /tmp/test $ ls -R .: b do_not_delete_me ./b: do_not_delete_me
As you can see, find tries to delete the folder b but fails because the folder is not empty. If you don't care for files in sub-directories, it gets a bit more complicated with find:
find . -mindepth 1 -maxdepth 1 -not -name do_not_delete_me -exec rm -rf -- {} +
The -mindepth/-maxdepth parameters tell find to ignore sub-directories, because we're not interested in their contents. This should also save some execution time - especially if the directory hierarchy is really deep.
While this works well, Bash's pattern matching offers an easier solution for this:
rm -rf !(do_not_delete_me)
As the manpage explains, the text enclosed by brackets is considered to be a pattern list, i.e. constructs like !(*.jpg|*.png) are perfectly valid. If you don't care for files in sub-directories, this might be the preferred way - it's shorter and maybe even faster than the solutions using find.
No matter which solution you choose, refrain from error-prone constructs like rm -rf `ls | grep -v do_not_delete_me`.