Bash Globbing – Exclude Specific Extensions in Filenames with Dots

bashfilenameswildcards

I'm doing some stuff with audio files, most but not all of which are mp3 files. Now I want to run some commands on only the files which are not mp3 files, or only those which don't have a .mp3 extension.

I consider myself pretty good at regular expressions, but not so much at file globbing, which is subtly different in unexpected ways.

I looked around and learned from other SO & SE answers that Bash has "extended globbing" that allows me to do this:

file ../foo/bar/*.!(mp3)

But some of my filenames have dots in them besides the one forming the filename extension:

../foo/bar/Naked_Scientists_Show_19.10.15.mp3
../foo/bar/YWCS_ep504-111519-pt1_5ej4_41cc9320.mp3_42827d48daefaa81ec09202e67fa8461_24419113.mp3
../foo/bar/eLife_Podcast_19.09.26.mp3
../foo/bar/gdn.sci.080428.bg.science_weekly.mp3

It seems the glob matches from the first dot onward, rather than from the last dot. I looked at the documentation but it seems they are far less powerful than regexes. But I didn't really grok everything as I don't spend that much time on *nix shells.

Have I missed some way that I can still do this with Bash globbing? If not, a way to achieve the same thing with find or some other tool would still be worth knowing.

Best Answer

*.!(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|!(*.*)).