I recently need to find a set of different file types of files, actually, not to find that set of files. An example is finding files are not images, in initial pattern would be like:


find -type f -! \( -iname *.png -o -iname *.gif\)

It focuses on files not directories, -! finds files not matching the following set of file types, they are *.png or *.gif1. -o means OR operator and -iname matches the file name with glob pattern case insensitively. As anyone can guess, this is most likely to expand to a larger set of types and it does become the following command:


find -type f -! \( -iname *.jpg -o -iname *.jpeg -o -iname *.png -o -iname *.gif -o -iname *.bmp \)

A better way to match is to use -iregex:


find -type f -! -iregex '.*\.\(jpe?g\|png\|gif\|bmp\)'

It’s only half of the length and much simpler in my opinion. Only thing I don’t like is you have to escape (a|b) into \(a\|b\).

Anyway, this got me thinking if there is a better way to do this, alternative find with preset sets of file types? Or maybe predefined patterns for references?


IMAGES='.*\.\(jpe?g\|png\|gif\|bmp\)'
CFILES='.*\.\(c\|h\)'
find -type f -! \( -iregex "$IMAGES" -o -iregex "$CFILES" \)
[1]A good practice is to single quote the pattern, for instance, '*.png', but I am sure there is no such images under the current directory, so that pattern wouldn’t be expanded to real file names by Bash or the shell you use.