Search code examples
linuxbashfindrm

How to "rm -rf" with excluding files and folders with the "find -o" command


I'm trying to use the find command, but still can't figure out how to pipe the find ... to rm -rf

Here is the directory tree for testing:

/path/to/directory
/path/to/directory/file1_or_dir1_to_exclude
/path/to/directory/file2_or_dir2_to_exclude
/path/to/directory/.hidden_file1_or_dir1_to_exclude
/path/to/directory/.hidden_file2_or_dir2_to_exclude

/path/to/directory/many_other_files
/path/to/directory/many_other_directories

Here is the command for removing the whole directory:

rm -rf /path/to/directory

But how to rm -rf while excluding files and folders?

Here is the man help for reference:

man find

-prune True;  if  the  file is a directory, do not descend into it.  If
      -depth is given, then -prune has no effect.  Because -delete im‐
      plies  -depth,  you  cannot  usefully use -prune and -delete to‐
      gether.
        For example, to skip the directory `src/emacs' and  all  files
      and directories under it, and print the names of the other files
      found, do something like this:
                find . -path ./src/emacs -prune -o -print

What's the -o in this find command? Does it mean "or"? I can't find the meaning of -o in the man page.

mkdir -p /path/to/directory

mkdir -p /path/to/directory/file1_or_dir1_to_exclude
mkdir -p /path/to/directory/file2_or_dir2_to_exclude

mkdir -p /path/to/directory/.hidden_file1_or_dir1_to_exclude
mkdir -p /path/to/directory/.hidden_file2_or_dir2_to_exclude

mkdir -p /path/to/directory/many_other_files
mkdir -p /path/to/directory/many_other_directories

I have tried to use this find command to exclude the .hidden_file1_or_dir1_to_exclude and then pipe it to rm, but this command does not work as expected.

cd /path/to/directory
find . -path ./.hidden_file1_or_dir1_to_exclude -prune -o -print | xargs -0 -I {} rm -rf {}

Solution

  • The meaning of rm -rf is to recursively remove everything in a directory tree.

    The way to avoid recursively removing everything inside a directory is to get find to enumerate exactly the files you want to remove, and nothing else (and then of course you don't need rm at all; find knows how to remove files, too).

    find . -depth -path './.hidden_file1_or_dir1_to_exclude/*' -o -delete
    

    Using -delete turns on the -depth option, which disables the availability of -prune; but just say "delete if not in this tree" instead. And indeed, as you seem to have discovered already, -o stands for "or".

    The reason -delete enables -depth should be obvious; you can't traverse the files inside a directory after you have deleted it.

    As an aside, you need to use -print0 if you use xargs -0. (This facility is a GNU extension, and generally not available on POSIX.)