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.