I'm trying to figure out my old skill in *nix.

What I'm trying to achieve is to remove every folders that does not contain a specific word.

This is the listing

➜  myfolder: pwd

➜  myfolder: ll locale

drwxr-xr-x  3 user  staff  102 23 Oct 11:13 bn_BD
drwxr-xr-x  3 user  staff  102 23 Oct 11:13 bn_IN
drwxr-xr-x  3 user  staff  102 22 Oct 19:03 en_CA
drwxr-xr-x  8 user  staff  272  9 Dec 16:10 en_US
drwxr-xr-x  3 user  staff  102 22 Oct 19:03 es
drwxr-xr-x  3 user  staff  102 21 Oct 13:18 fr
drwxr-xr-x  3 user  staff  102 23 Oct 11:13 pt
drwxr-xr-x  3 user  staff  102 21 Oct 13:18 ru
drwxr-xr-x  3 user  staff  102 21 Oct 13:18 th_TH

Now I want to remove everything except en_US

I don't know what is the not in the command

rm -rf locale/[!somethinghere]


With GNU find (and possibly other versions too), you can use an exclamation mark to reverse a search -- so this will find and delete everything in the current directory except 'en_us':

find . ! -name 'en_US' -delete
##  If you only want to delete directories:
find . -type d ! -name 'en_US' -delete
##  If you *don't* want recursiveness:
find . -maxdepth 1 -type d ! -name 'en_US' -delete
##  If you want to avoid deleting dotfiles:
find . -maxdepth 1 -type d ! -name 'en_US' -a ! '.*' -delete

Some versions of find don't have the -delete option; instead you could use one of:

find . -maxdepth 1 -type d ! -name 'en_US' -exec rm -r '{}' +
find . -maxdepth 1 -type d ! -name 'en_US' -a ! '.*' -exec rm -r '{}' +

Alternatively, since you mentioned in a comment that you have zsh,

setopt extendedglob  ##  this may already be set in you .zshrc
rm ^*en_US*
##  If you only want to delete directories:
rm ^*en_US*(/)
| improve this answer | |
  • Yours very similar to what I found except I can't seem to use -delete? – Ali Dec 9 '13 at 23:17
  • What does find --version give you? Are you using GNU find, or something else? – evilsoup Dec 9 '13 at 23:18
  • I just use find that built-in from my Mac OS X? – Ali Dec 9 '13 at 23:19
  • 1
    Ahh. You should have specified OSX in your tags (I was wrongly assuming linux). It uses the BSD versions of the various command-line tools, while I'm used to the GNU versions that most linuxen use. Maybe try the new version of the command in the latest version of my answer. – evilsoup Dec 9 '13 at 23:26
  • Sorry about that @evilsoup.. I should have mention that >.< – Ali Dec 9 '13 at 23:28

A three-line solution will work (tested with Linux bash):

tar cvf foo $(find locale -name en_US)
rm -fr locale
tar xvf foo

That is:

  • Save what you want to keep
  • Delete original
  • Restore what you saved

I played around with something like find locale \( -name en_US -true \) -o -print | xargs ..., but this seems more complicated. And more risky, if one's Unix skills are rusty.

| improve this answer | |
  • I would prefer the second option only if I can figure out why it keep matching "en_US" too – Ali Dec 9 '13 at 22:44

This is what I end up doing.

find locale -type d ! -name 'en_US' ! -name 'locale' | xargs rm -rf

The reason I have to include "locale" as well because the xargs returned locale locale/bn_BD locale/bn_IN locale/en_CA locale/es locale/fr locale/pt locale/ru locale/th_TH

Now I have these deleted

#   deleted:    locale/bn_BD/goggles.webmaker.org.json
#   deleted:    locale/bn_IN/goggles.webmaker.org.json
#   deleted:    locale/en_CA/goggles.webmaker.org.json
#   deleted:    locale/es/goggles.webmaker.org.json
#   deleted:    locale/fr/goggles.webmaker.org.json
#   deleted:    locale/pt/goggles.webmaker.org.json
#   deleted:    locale/ru/goggles.webmaker.org.json
#   deleted:    locale/th_TH/goggles.webmaker.org.json
| improve this answer | |
  • 1
    You shouldn't pipe find output to xargs, unless you use find … -print0 | xargs -0 …. – slhck Dec 10 '13 at 8:11

You can do it using bash expansion: rm -rf path/to/!(something.*)


As mentioned in comments below, this command works if extended globbing format is enabled.

You can enable it by command shopt -s extglob in bash or setopt kshglob in zsh.

| improve this answer | |
  • 1
    I don't know if it is because of zsh but I'm getting this - zsh: no matches found: locale/!(en_US.*)/ – Ali Dec 9 '13 at 22:38
  • You need to use shopt -s extglob to enable this behaviour in bash; the syntax for zsh is different. – evilsoup Dec 9 '13 at 22:51
  • @evilsoup I'm not sure what is this mean? shopt -s extglob ? – Ali Dec 9 '13 at 22:55
  • If you were using bash, you would have to enter the command shopt -s extglob to enable extended globbing, which allows you to use the !(pattern*) syntax in this answer. But you are using zsh, which uses a different sort of extended glob. I think that if you enter the command setopt kshglob, you should be able to use this answer. – evilsoup Dec 9 '13 at 23:01

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.