11

Suppose under the current directory, there are multiple sub-directories, and one is called A.

How to delete all sub-directories except A with Bash?

11 Answers 11

24

Bash has extended globbing (first test, then remove the echo):

shopt -s extglob
echo rm -rf !(A)
3
  • (before I forget - you might disable CSH style history completion, in Bash3 there's a bug while ignoring !(. set +H
    – TheBonsai
    Jun 4, 2009 at 19:38
  • +1 Bash if full of surprises! Sometimes they are nice like this one. Nov 16, 2010 at 1:46
  • Wauw very nice indeed! Works as expected. With shopt | grep ext You can see if the plugin is enabled. Nov 21, 2015 at 21:24
14
find -maxdepth 1 -type d -not -name A -not -name "." -exec rm -ir {} \;
0
10

What about:

mv A /tmp/
rm * -rf
mv /tmp/A .

This avoids some of the "scariness" of a typo in the other commands.

Please be aware to not be in the root (/) folder when running the rm * -rf command above.

2
  • 1
    +1 for doing the sane thing instead of hoping the tools are smart enough. still, one night, when i was just learning Linux, i copied the kernel to /tmp before doing my first recompile. it didn't succeed, but erased the old one, so i said 'no sweat, i have a copy' only to find that a midnight cron task had erased it with every file more than a week old in /tmp! i had to recompile again and this time do it right, or no reboot for me!
    – Javier
    Jun 4, 2009 at 21:16
  • best answer posted :)
    – warren
    Nov 16, 2010 at 2:59
4

Something like

find . -type d -not -name A -exec rm -ir {} \;

should do.

edit

It should really be

find . -type d -maxdepth 1 -not -name A -exec rm -ir {} \;

to prevent find from recursing below A.

3
  • 3
    But, this would also remove any sub-directories below A like A/foo and A/bar, but would preserve A/A.
    – TCampbell
    Jun 4, 2009 at 19:30
  • Cool! Questions and answert get migrated from SO to SF... but my edit above was lost...
    – agnul
    Jun 4, 2009 at 19:47
  • It should be better to swap maxdepth and type arguments like this : find . -maxdepth 1 -type d -not -name A -exec rm -ir {} \;
    – Spawnrider
    Feb 11, 2019 at 14:08
1

i usually do this by working up an ls command that gets it right first. i'm not at a unix machine, but something like:

ls -lda "[^A]"

Once you get it right, pipe it to a command

ls -lda "[^A]" | xargs rm -rf

Feel free to edit above if I've got my regular expression wrong...

1
1

If you want to be more flexible but manual you can do:

ls > /tmp/foo
edit /tmp/foo as you like
xargs -a /tmp/foo rm -r

That way you can do general munging.

1

Here's one way. Be careful with this sort of thing, though, it's so powerful that it can only be used for good or evil...

find * -type d | grep -v "^A" | xargs rm -rf
1
  • Actually -v already negates the values, so "^" is not required, it's -v name_to_keep
    – Braikar
    Apr 23, 2021 at 9:11
1

Don't use find as some people have shown with -exec and rm without passing -print0 to find and -0 to xargs. It will get confused on file names with spaces or newlines:

$ mkdir 'foo foo'
$ mkdir foo$'\n'foo 
$ find . -type d -exec rm -ir {} \;
rm: cannot remove directory `.'
rm: remove directory `./foo\nfoo'? y
find: `./foo\nfoo': No such file or directory
rm: remove directory `./foo foo'? y
find: `./foo foo': No such file or directory

Instead use find -print0 with xargs -0 , '-exec command {} +', or -delete if your find supports it.

1

In addition to the earlier example of:

find -maxdepth 1 -type d -not -name A -not -name "." -exec rm -ir {} \;

you can also do:

find some/subdir -mindepth 1 -maxdepth 1 -type d -not -name A -exec rm -ir {} \;

to avoid having to cd some/subdir first.

0

find ...ROOT_FOLDER.../* -type d | grep -v FOLDER_TO_KEEP | xargs rm -rf

You must log in to answer this question.