JoulinRouge
JoulinRouge

Reputation: 466

find command: delete everything but one folder

I have this command:

find ~/Desktop/testrm -mindepth 1 -path ~/Desktop/testrm/.snapshot -o -mtime +2 -prune -exec rm -rf {} +

I want it to work as is, but it must avoid to remove a specific directory ($ROOT_DIR/$DATA_DIR).

Upvotes: 0

Views: 183

Answers (1)

Socowi
Socowi

Reputation: 27215

You can exclude individual paths using the short circuiting behavior of -o (like you already did with ~/Desktop/testrm/.snapshot). However, for each excluded path you also have to exclude all of its parent directories. Otherwise you would delete a/b/c by deleting a/b/ or a/ with rm -rf.

In the following script, the function orParents generates a part of the find command. Example:
find $(orParents a/b/c) ... would run
find -path a/b/c -o -path a/b -o -path a -o ....

#! /usr/bin/env bash
orParents() {
  p="$1"
  while
    printf -- '-path %q -o' "$p"
    p=$(dirname "$p")
    [ "$p" != . ]
  do :; done
}
find ~/Desktop/testrm -mindepth 1 \
  $(orParents "$ROOT_DIR/$DATA_DIR") -path ~/Desktop/testrm/.snapshot -o \
  -mtime +2 -prune -exec rm -rf {} +

Warning: You have to make sure that $ROOT_DIR/$DATA_DIR does not end with a / and does not contain glob characters like *, ?, and [].
Spaces are ok as printf %q escapes them correctly. However, find -path interprets its argument as a glob pattern independently. We could do a double quoting mechanism. Maybe something like printf %q "$(sed 's/[][*?\]/\\&/' <<< "$p")", but I'm not so sure about how exactly find -path interprets its argument.

Alternatively, you could write a script isParentOf and do ...

find ... -exec isParentOf "$ROOT_DIR/$DATA_DIR" {} \; -o ... 

... to exclude $ROOT_DIR/$DATA_DIR and all of its parents. This is probably safer and more portable, but slower and a hassle to set up (find -exec bash -c ... and so on) if you don't want to add a script file to your path.

Upvotes: 1

Related Questions