20

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) .

22

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

setopt extended_glob
rm -- ^*.dmg
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)
  • This doesn't seem to work. When I run rm -r secrets/!(directory) it keeps asking for number expected or sometimes it gives me event not found: directory – CMCDragonkai Jun 1 '14 at 13:46
  • @CMCDragonkai “number expected” indicates that you didn't set the ksh_glob option. “event not found: directory” indicates that you're running bash (which also understands the !(…) syntax, but only after shopt -s extglob). – Gilles 'SO- stop being evil' Jun 1 '14 at 19:01
  • How could I be running bash when I was in my zsh shell? – CMCDragonkai Jun 2 '14 at 1:09
  • For me, setopt ksh_glob; echo !(a|b) works while setopt ksh_glob; echo !(a) doesn't ("number expected")... – ThiefMaster Feb 15 '15 at 14:09
  • @ThiefMaster I had same issue. Is it a bug? – CodeTower May 11 '15 at 10:02
5

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
          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
  • unfortunately, whenever I use find in my mac, it just exits the current zsh session without any notice. Do you have the same problem ? find [Process completed] – nXqd Nov 24 '13 at 15:51
  • 1
    @nXqd see updated answer for a way to do it without find. I suggest you post a question about your find issue though, that should not happen. If you do, include the output of type -a find. – terdon Nov 24 '13 at 16:02
  • 1
    Even in bash, there is a simpler solution: rm !(*.dmg), after shopt -s extglob. – Gilles 'SO- stop being evil' Nov 24 '13 at 21:23
  • @terdon thanks for your debugging tips in shell. its output seems to be correct : find is a shell function find is /usr/bin/find – nXqd Nov 24 '13 at 22:26
  • @nXqd that is not correct, it should not be a shell function, it should only be /usr/bin/find. You have a function called find defined in one of your bash config files. Since you're on OSX, that's probably ~/.profile. – terdon Nov 25 '13 at 0:30
3

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

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.