I don't think this question is about avoiding rm -rf
, but about avoiding ending up in states where the operation is unsafe or using it in ways that could have uncertain consequences. When it comes to spelling errors, you could catch some of them using tools like shellcheck
and setting certain shell variables (e.g. failglob
in bash
, and nounset
for example). Still, ultimately it comes down to forming a habit of reading your code before you run it and testing it on throwaway or backed-up data.
I don't see the use of complicating it too much. If you want to delete all the directory contents, it would be the most straightforward approach to delete the directory entirely and recreate it.
rm -rf -- "$directory" && mkdir -p -- "$directory" || exit
Note the quoting of $directory
. It is needed because the string in $directory
would otherwise be split into terms so that if the variable's value is foo bar
, the code would remove the two names foo
and bar
. The words would additionally be expanded as file name globbing patterns, making it dangerous to remove a directory called literally *
. We use the --
to end option parsing before the actual directory name, enabling us to work with directories called, e.g., -f
or --version
.
The exit
above would be executed if either rm
or mkdir
failed. The code would naturally also reset any ownership and other metadata on the directory it recreates.
Another approach would be using find
.
find "$directory/." ! -name . -delete
The above uses the -delete
predicate of find
, which is non-standard but commonly implemented. It deletes everything beneath the given directory except for the directory itself. In this case, the original directory entry is left in place rather than recreated.
Using portable find
, we would write this
find "$directory/." -depth ! -name . -exec rm -rf {} +
Note that your code would execute cd -
even if your first cd
failed, possibly leaving your script in an unexpected working directory. Your code also fails in removing hidden names and it would fail with "argument list too long" if the globbing pattern expands to too many names. If *
does not match anything, it will terminate a script running with the failglob
(or equivalent) shell option set (note that this is a good thing if the directory was expected to be non-empty, but was empty).