Remove all but one ( or more ) kind of filetype

I’ve been trying to find a command to remove all files in a folder but not a kind of filetype. But I seems to not have any luck. What I’ve tried so far :

set extended_glob
rm !(*.dmg)
# this returns zsh:number expected

rm ./^*.dmg
# this returns no matches found

The version of zsh I’m using is zsh 5.0.2 (x86_64-apple-darwin13.0.1) .


Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

The extended_glob option gives you zsh’s own extended glob syntax.

setopt extended_glob
rm -- ^*.dmg
rm -- ^*.(dmg|txt)

This removes files without an extension (e.g. README). If you want to keep those files, you can use the ~ operator to limit the matches:
setopt extended_glob
rm -- *.*~*.dmg

If you want to delete files in subdirectories as well, you can use ** for recursive globbing. Pass the . glob qualifier to restrict the matching to regular files, or use ^/ instead of . to match all non-directories (including e.g. symbolic links). (Note that rm -r wouldn’t help you since it would either delete a directory and all its contents, or not descend into a directory at all.)
rm -- **/^*.(dmg|txt)(.)

You can set the ksh_glob option to get ksh globs. Beware that in the common case where the negative pattern is the last thing in the word, zsh may parse the parentheses as glob qualifiers (it doesn’t do this in ksh emulation mode).
setopt ksh_glob
rm -- !(*.dmg|*.txt)
setopt no_bare_glob_qual
rm -- !(*.dmg)

Method 2

You can use find instead of your shell:

find . -mindepth 1 -maxdepth 1 ! -name "*.dmg" -delete

From man find:
   ! expr True  if  expr  is false.  This character will also usually need
          protection from interpretation by the shell.
   -name pattern
          Base of file name (the path with the leading directories removed)
          matches shell pattern pattern. 
          Delete  files; true if removal succeeded.  If the removal failed,
          an error message is issued.  If -delete fails, find's exit status
          will be nonzero (when  it eventually exits).  Use of -delete 
          automatically turns on the -depth option.

If you can’t use find for whatever reason, here’s a way to do it with zsh (or other shells). zsh being zsh, there is probably a simpler way of doing this but since I’m a bash guy, this is what I came up with:
for file in *; do if [[ ! "$file" == *.dmg ]]; then rm $file; fi; done

Method 3

Another way to remove files is with find, xargs and rm:

find . -mindepth 1 -maxdepth 1 ! -name '*.dmg' -print0 | xargs -0 rm

Method 4

One can also use fd (git-project) or fdfind on debian systems, which has a very user-friendly interface.

The command is

fd --exclude='*.dmg' --hidden --exec rm
fdfind --exclude='*.dmg' --hidden --exec rm # For Debian systems
fd -E '*.dmg' -H -x rm                      # Short notation

Method 5

find  path -mindepth 1 -maxdepth 1 -type f ! -name "*.dmg" -exec rm -vf {} ;

All methods was sourced from or, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Notify of
Inline Feedbacks
View all comments