Use find to find certain directory and delete all files in it except one directory

find

I'm having the following problem. I have a directory structure and want to delete everything that is within a directory named Caches. Everything should be delete except all directories named Snapshots. I can't figure out how to do this with find or any other command I know.

The reason I'm asking: On iOS every app has its own Caches directory. These sometimes don't get cleared properly, depending on the app. With the solution to this answer, one would be able to clear Caches on iOS, and therefore optimize the disk space, when the devices' drive is mounted on another computer, e.g. with FUSE (iExplorer).

This is what I have so far:

find . 2>/dev/null -type d -name "Caches" -maxdepth 3 -print

This returns something like:

./Library/Caches

When I do a ls ./Library/Caches I see all contents and the Snapshots directory, which I want to exclude because ultimately I want to -delete everything except this one.


I want something like this:

  Before:                            After:

  .                                  .
  ├── a                              ├── a
  │   ├── a                          │   ├── a
  │   └── Caches                     │   └── Caches
  │       ├── a                      │       └── a
  │       │   └── Snapshots          │           └── Snapshots
  │       │       └── a              │               └── a
  │       ├── b                      └── b
  │       │   └── a                      └── c
  │       └── c                  
  └── b
      ├── c
      └── Caches
          ├── a
          │   └── foo
          │       └── a
          └── b
              └── a

Best Answer

find . -depth -print0 | perl -0lne '
  if ("$_/" =~ m{/Caches(/.*)}s && $1 !~ m{/Snapshots/}) {rmdir $_ or unlink $_}'

If your find doesn't support -print0, you can replace it with -exec printf '%s\0' {} +.

The idea is to print the list of files NUL-terminated (as 0 is the only byte that can't occur in a file path) and use perl's -n with -0 option to run some code for each of those filenames (with $_ set to the filename).

With -depth, files are printed before their parent directory. We remove only files or directories (assuming they are empty which is why it's important to process the list in depth order) if their path contains /Caches/ not followed by /Snapshosts/.

Related Question