find: list all directories except those with numbers in their names and their children

Use -prune to ignore those directories:

find /path/to/directory -name '*[0-9]*' -prune -o -type d -print

though if you're on a gnu setup you may want to use the C locale when running the above, see Stéphane's comment below.


If you don't want find to descent beyond a certain match you should use -prune and not e.g. -path or filtering the output of find with grep -v

To test things out make an environment with some extra files and subdirs, so you can check your find not to display unwanted material:

mkdir -p tmp/2/abc/def
touch tmp/2/abc/def/file1
mkdir -p tmp/2/abc/9876/xyz
touch tmp/2/abc/9876/xyz/file2
tree tmp/

gives:

tmp
└── 2
    └── abc
        ├── 9876
        │   └── xyz
        │       └── file2
        └── def
            └── file1

If you do find tmp/2/abc \! -path "*[0-9]*" as suggested by @terdon, the output will be empty, because -path doesn't just take into account the directories starting below abc but the whole path, which includes 2. So that is not what you want.

If you do find tmp/2/abc -type d | grep -vE '/[0-9-]+(/|$)', as suggested by @cas, you'll find that it doesn't print anything either, again because it matcheds not just the files down from where you're searching but also the directory named 2. Apart from that this would require find to first walk the whole tree under 9876 and if there are a few hundred thousand items there the walking (and filtering) will take a noticable amount of time.

If you do:

find tmp/2/abc -type d -name '[!0-9]*' -print 

you will find that the output includes the path tmp/2/abc/9876/xyz. To get rid of that cut off what you don't want with -prune:

find tmp/2/abc -type d -name '[!0-9]*' -print -o -name '[0-9]*' -prune

which gives:

tmp/2/abc
tmp/2/abc/def

you can slightly improve the on the efficiency of that by swapping the pruning and printing which is what @don_cristti did in his enhancement of this answer.


I think you're looking for -path:

   -path pattern
          File  name matches shell pattern pattern.  The metacharacters do
          not treat `/' or `.' specially; so, for example,
                    find . -path "./sr*sc"
          will print an entry for a directory called `./src/misc' (if  one
          exists).  

It searches the entire path of each file/directory for the given pattern. So, if you have a directory structure like this:

$ tree
.
├── 111
│   └── foo
│       └── bar
├── bar
│   └── foo
│       └── baz
└── foo
    └── 111
        └── bar

9 directories, 0 files

You can use find like this:

$ find /path/to/directory/ \! -path "*[0-9]*" 
.
./foo
./bar
./bar/foo
./bar/foo/baz

Or, with GNU find:

find /path/to/directory/ -not -path "*[0-9]*" 

Since this will have to descend into each directory to check its name, it will be significantly slower on large directory trees than @Anthon's solution with prune. If you don't have thousands of directories, though, it should be fine.