Delete all files/directories except two specific directories

12,677

Solution 1

find's -prune comes to mind, but it's a pain to get it to work for specific paths (icecream/cupcake/) rather than specific directories (cupcake/).

Personally, I'd just use cpio and hard-link (to avoid having to copy them) the files in the directories you want to keep to a new tree and then remove the old one:

find test -path 'test/icecream/cupcake/*' -o -path 'test/mtndew/livewire/*' | cpio -padluv test-keep
rm -rf test

That'll also keep your existing directory structure for the directories you intend to keep.

Solution 2

This command will leave only the desired files in their original directories:

find test \( ! -path "test/mtndew/livewire/*" ! -path "test/icecream/cupcake/*" \) -delete

No need for cpio. It works on Ubuntu, Debian 5, and Mac OS X.

On Linux, it will report that it cannot delete non-empty directories, which is exactly the desired result. On Mac OS X, it will quietly do the right thing.

Solution 3

Everything "except" is why we have if-statements; and why os.walk's list of directories is a mutable list.

for path, dirs, files in os.walk( 'root' ):
    if 'coke' in dirs:
        dirs.remove('coke')
        dirs.remove('pepsi')

Solution 4

You could do something based on Python's os.walk function:

import os
for root, dirs, files in os.walk(top, topdown=False):
    for name in files:
        os.remove(os.path.join(root, name))
    for name in dirs:
        os.rmdir(os.path.join(root, name))

...just add something to ignore the paths you're interested in.

Solution 5

find /path/to/test/ -depth -mindepth 1 \
! -path "/path/to/test/icecream/cupcake/*" \
! -path "/path/to/test/icecream/cupcake" \
! -path "/path/to/test/icecream" \
! -path "/path/to/test/mtndew/livewire/*" \
! -path "/path/to/test/mtndew/livewire" \
! -path "/path/to/test/mtndew"
 -delete -print

It's a bit tedious to write all the paths to preserve but thi is the only way to use find alone.

Share:
12,677
phuzion
Author by

phuzion

Hi. I enjoy life and computers. I dislike writing biographies about myself. Getting to know me personally is 100 times better than reading a short text blurb about me, so follow me on twitter or something.

Updated on June 19, 2022

Comments

  • phuzion
    phuzion almost 2 years

    So, there seems to be a few questions asking about removing files/directories matching certain cases, but I'm looking for the exact opposite: Delete EVERYTHING in a folder that DOESN'T match my provided examples.

    For example, here is an example directory tree:

    .
    |-- coke
    |   |-- diet
    |   |-- regular
    |   `-- vanilla
    |-- icecream
    |   |-- chocolate
    |   |-- cookiedough
    |   |-- cupcake
    |   |   |-- file1.txt
    |   |   |-- file2.txt
    |   |   |-- file3.txt
    |   |   |-- file4.txt
    |   |   `-- file5.txt
    |   `-- vanilla
    |-- lol.txt
    |-- mtndew
    |   |-- classic
    |   |-- codered
    |   |-- livewire
    |   |   |-- file1.txt
    |   |   |-- file2.txt
    |   |   |-- file3.txt
    |   |   |-- file4.txt
    |   |   `-- file5.txt
    |   `-- throwback
    `-- pepsi
        |-- blue
        |-- classic
        |-- diet
        `-- throwback
    

    I want to delete everything but the files in test/icecream/cupcake/ and test/mtndew/livewire/. Everything else can go, including the directory structure. So, how can I achieve this? Languages I wouldn't mind this being in: bash or python.

  • phuzion
    phuzion almost 15 years
    Tried this, and it says the following: find /home/phuzion/test/ \( -prune 'icecream/cupcake/' \) -delete find: paths must precede expression
  • SourceSeeker
    SourceSeeker almost 15 years
    From the man page for find: "Because -delete implies -depth, you cannot usefully use -prune and -delete together."
  • SourceSeeker
    SourceSeeker almost 15 years
    The correct syntax would be more like "find $directory -path ./pepsi/diet -prune -o -exec some-command '{}' \;" anyway to eliminate that error message.
  • SourceSeeker
    SourceSeeker almost 15 years
    In your first example, it will rm -r test/icecream which will include cupcake (for example). So, even though you've grep -v the cupcakes, they still get "eaten". The second example has a couple of typos and unbalanced single quotes. There should be a space after sed and the zero should be an ampersand.
  • SourceSeeker
    SourceSeeker almost 15 years
    This finds test and deletes it, thus deleting the directories that you're trying to save.
  • Patrick Webster
    Patrick Webster almost 15 years
    This finds test and does NOT delete it, because it is non-empty. Are there any modern flavors of Linux or Unix where this command fails to work?
  • SourceSeeker
    SourceSeeker almost 15 years
    Sorry, I made a mistake in setting up the test on my system. Your example works correctly for me.
  • SourceSeeker
    SourceSeeker almost 15 years
    You still have unbalanced single quotes around the paths. For example, it should be "rm './test/icecream/cupcake/'"
  • kevinmicke
    kevinmicke over 7 years
    Though it wasn't the original question, some may find this useful: you can preserve the entire directory structure and only delete files not excluded with test by adding the -type f flag before -delete.