Bash globbing that matches all files except those with a specific extension, that works on filenames that include dot characters

*.!(mp3) matches on foo.bar.mp3 because that's foo. followed by bar.mp3 which is not mp3.

You want !(*.mp3) here, which matches anything that doesn't end in .mp3.

If you want to match files whose name contains at least one . (other than a leading one which would make them a hidden file) but don't end in .mp3, you could do !(*.mp3|!(*.*)).


There's also the GLOBIGNORE variable:

The GLOBIGNORE shell variable may be used to restrict the set of file names matching a pattern. If GLOBIGNORE is set, each matching file name that also matches one of the patterns in GLOBIGNORE is removed from the list of matches. If the nocaseglob option is set, the matching against the patterns in GLOBIGNORE is performed without regard to case. The filenames . and .. are always ignored when GLOBIGNORE is set and not null. However, setting GLOBIGNORE to a non-null value has the effect of enabling the dotglob shell option, so all other filenames beginning with a ‘.’ will match. To get the old behavior of ignoring filenames beginning with a ‘.’, make ‘.*’ one of the patterns in GLOBIGNORE. The dotglob option is disabled when GLOBIGNORE is unset.

$ touch a.b.mp3 .c.mp3 foo.tgz .bar
$ echo *
a.b.mp3 foo.tgz
$ shopt -s extglob; echo *.!(mp3)
a.b.mp3 foo.tgz
$ GLOBIGNORE='*.mp3'; echo *
.bar foo.tgz

the simplest way I can think of:

find ${path-to-folder} -type f  | grep -vE ".*\.mp3$"

You find all files in a folder and pipe it to a reverse extended grep:

Find all files in a location

find ${path-to-folder} -type f 

Reverse grep and use a regex to filter by extension

grep -vE ".*\.mp3$"

Grep flags:

  • -v reverse grep

  • -E extended grep (use of regex)

Regex explained:

  • .* means from 0 to whatever char count, any possible char

  • \. looks for the actual dot

  • mp3 for the string that forms the extension

  • $ stands for EOL