4

I want to delete everything under some directory /path/to/foo, EXCEPT those sub-directories that match the meta-pattern

/path/to/foo/<DIGITS>/

For example, if the contents under /path/to/foo are initially like this:

/path/to/foo
├── 0/
│   ├── a
│   └── b
├── 232532/
├── 42
├── 73/
│   ├── d
│   └── e
├── 8xyz/
│   ├── i
│   └── j
├── _bar/
│   ├── x
│   ├── y
│   └── z
├── .baz/
│   ├── f
│   └── frobozz/
│       ├── g
│       └── h
└── quux/
    └── 123/

...I want to end up with

/path/to/foo
├── 0/
│   ├── a
│   └── b
├── 232532/
└── 73/
    ├── d
    └── e

I'm looking for a find ... -delete-based incantation, or a suitable zsh glob pattern (for rm -r), that will do this. I am using Linux.

  • 3
    @JeffSchaller no, that will just retouch the directory you created before. Just use: mkdir -p 0/{a,b} 232532 73/{d,e} 8xyz/{i,j} _bar/{x,y,z} .baz/f .baz/frobozz/{g,h} quux/123; touch 42 – terdon Aug 8 '17 at 17:46
  • @terdon: I'm using Debian – kjo Aug 8 '17 at 17:48
5

With zsh:

set -o extendedglob # best in ~/.zshrc
rm -rf /path/to/foo/^<->(D) /path/to/foo/<->(^-/)
  • ^something is not something (similar to ksh's !(something))
  • <-> is <x-y> to match decimal integers from x to y, but with none of the bounds provided (so matches any sequence of decimal digits, similar to ksh's +([0-9])).
  • (D) a glob qualifier to include hidden files (Dot files)
  • (^-/) a glob qualifier to say only files that are not of type directory after symlink resolution (remove the - if you also want to remove symlinks to directories).
4

If you have access to GNU find, you can run:

find . -maxdepth 1 -not \( -type d -regex '.*/[0-9]*$' \) -exec rm -r {} +

The idea is to find those top level files that aren't i) directories and ii) whose name doesn't consist of nothing but numbers.

I strongly urge you to run it once with echo instead of rm -r to see what it will do.

4
cd /path/to/foo && \
find . -maxdepth 1 ! -type d -delete -o \
   ! -name . -name '*[!0-9]*' -exec rm -rf {} +

Which reads as:

  • change over to the /path/to/foo directory and once successfully there
  • invoke find to go just one level deep and delete any non-directories there.
  • for all others, i.e., subdirectories in your /path/to/foo dir, reject those with the names of ".", and what remains out of these, select those that have at least one nondigit in it.
  • The selected dirs are then led to the abbatoir, thereby ensuring the survival of digit-only dirs.
3

Apart from zsh, you can't select non-directories using wildcards (you can only make a wildcard pattern apply to all file types, or apply to directories and symlinks to directories by putting a / at the end).

In any shell, you can loop over the files and exclude the ones you want to keep. To enumerate all files including dot files and . and .., use the two wildcard patterns * .*. It's possible that * will not match any file and so will remain unexpanded; in this case, rm -rf is executed on *, which does nothing since rm -f ignores non-existent arguments.

for x in * .*; do
  case "$x" in
    .|..: continue;;                              # skip the . and .. entries
    *[!0-9]*) rm -rf -- "$x";;                    # not just digits -> delete
    *) if ! [ -d "$x" ]; then rm -f -- "$x"; fi   # just digits -> delete if not a directory
  esac
done
1
find ./* ! \( -type d -regex "./[0-9]+\(/.*\)?" \) -delete

or, if foo have too many files and the argument limit excess happens, use:

find . ! -path . ! \( -type d -regex "./[0-9]+\(/.*\)?" \) -delete

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.