Super User is a question and answer site for computer enthusiasts and power users. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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]

share|improve this question
up vote 3 down vote accepted

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

share|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/
#   deleted:    locale/bn_IN/
#   deleted:    locale/en_CA/
#   deleted:    locale/es/
#   deleted:    locale/fr/
#   deleted:    locale/pt/
#   deleted:    locale/ru/
#   deleted:    locale/th_TH/
share|improve this answer
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.

share|improve this answer
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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .