On Mac OS X terminal, how can we remove all files and folders, including hidden ones, recursively, except one directory?
Thank you.
WARNING: running any command to recursively delete files is dangerous. Always, always test before blindly running such dangerous commands, and never run them as the root user (or with sudo etc.), ever.
This is relatively easy to do with a bit of bash, you could even make it an argument you pass in if you are going to do this a lot. But, for a once off, try something like my simple test below.
First, I will create some folders for testing:
cd test; mkdir -p one two three four five six .hidden
Then, I will create a bash script (in this case rm.bash) with the following content:
for i in `ls -A`
do
if [ ! "${i}" == 'three' ] && [ ! "${i}" == $0 ]; then
echo "would delete ${i}"
else
echo "not deleting ${i}"
fi
done
To explain: ls -A
will print all files except the current folder and the parent (.
and ..
). We use this list with a for
loop to iterate through all the contents. I have decided (arbitrarily) that the only folder we will leave is "three" and that I don't want to delete the rm.bash
script itself.
Hence, the if
condition will only be true as long as the file/folder in question is not named either three
or rm.bash
($0
means this works no matter what you call the file).
Rather than running this immediately with rm -rf ${i}
(which is dangerous), I have instead used a couple of echo
commands to show what will be deleted and what will not.
Here was my sample output:
would delete five
would delete four
would delete one
not deleting rm.bash
would delete six
not deleting three
would delete two
Once you have verified that the script is taking the correct action, not deleting things that it should not etc., then you can change the code to this:
for i in `ls -A`
do
if [ ! "${i}" == 'three' ] && [ ! "${i}" == $0 ]; then
rm -rf ${i}
fi
done
An alternative. Same caveats apply as @Adam C's answer; don't run this without testing it.
Some demo data:
$ mkdir testing; cd testing
$ mkdir one two three four "fi ve"
$ touch one/one.txt two/two.txt three four/keep.txt fi\ ve/.hidden
Simulate deleting all but four/
$ find . -mindepth 1 -path ./four -prune -o -exec echo would remove "{}" +
Output:
would remove ./fi ve ./fi ve/.hidden ./two ./two/two.txt ./one ./one/one.txt ./three
Notes:
-mindepth 1 prevents including /.
Prune removes path four/ from processing, or...
-o means "or" (either prune the left, or exec on the right)
High speed / low resource use:
find will stop processing a tree once pruned (no wasted operations)
One xargs-style list is built (with +), removal happens in one command.
To process one at a time, replace + with \;
Spaces are handled correctly within " "
-delete is shorter but may not operate on directories
find won't follow symlinks by default