How do I delete files of certain extension that don't have a given string in their filename?

I have a directory with files of many extensions in it. I would like to recursively delete *.srt files (and *.srt files only) which don't end with -en.srt (where srt is an extension). I've come up with the following solution and it seems to work fine, however, I'd like to know whether it is 100% correct.

find . -name "*.srt" ! -name "*-en.srt" -type f -exec rm -rf {} \;

! -- First read the answer completely then use it if you like it -- !

Your command is correct, however there is no need to use -rf as rm parameters. because you are removing files and not directories.

Another clear way to write it is (it's almost same as your command):

find -name '*.srt' -and -not -name '*-en.srt' -type f -exec rm '{}' \;

or as @steeldriver suggested you can use:

find -name '*.srt' -and -not -name '*-en.srt' -type f -ok rm '{}' \;

It will ask for your permission to remove each founded file.

You can also use -delete instead of rm {} \; however be aware of its dangers:

Don't forget that the find command line is evaluated as an expres‐ sion, so putting -delete first will make find try to delete everything below the starting points you specified. When testing a find command line that you later intend to use with -delete, you should explicitly specify -depth in order to avoid later surprises. Because -delete implies -depth, you cannot usefully use -prune and -delete together.


It is always a good idea to test what is going to happen before doing the actual job, so I suggest running:

find -name '*.srt' -and -not -name '*-en.srt' -type f | grep -i en.srt

If it return nothing then the actual command will work without any problem and you are good to go... or even:

find -name '*.srt' -and -not -name '*-en.srt' -type f | less

to check what's going to be removed.

And do not forget to quote '{}':

(when find is being invoked from a shell) it should be quoted (for example, '{}') to protect it from interpretation by shells.


Let’s do it solely with bash globbing: With the extglob and globstar options enabled,

rm **/!(*-en).srt

deletes every file ending in .srt excluding anything ending in -en.srt from the current as well as any subdirectory.
If you‘re not sure about an expansion like this, test by prepending echo (see example below).

Example

$ tree
.
├── 01.srt
├── 02.srt
├── no-en.srt
├── not-en.srt
├── subdir
│   ├── 01.srt
│   ├── 02.srt
│   ├── no-en.srt
│   └── not-en.srt
└── unrelated.png
$ shopt -s extglob globstar
$ echo rm **/!(*-en).srt
rm 01.srt 02.srt subdir/01.srt subdir/02.srt
$ rm **/!(*-en).srt
$ tree
.
├── no-en.srt
├── not-en.srt
├── subdir
│   ├── no-en.srt
│   └── not-en.srt
└── unrelated.png

Explanations

  • **/ – with the globstar option enabled this matches any number of directories and subdirectories
  • !(*-en) – with the extglob option enabled this matches anything except the given pattern, so anything not ending in -en