3

I know that there are quite a lot of answered questions about how to delete all files with or without a certain string in their names, as well as ones about how to delete all subfolders with a certain string in their names.

Yet, there are no questions regarding how to delete all subfolders without a certain string in there names.

So, as I have recently ran into such a problem, is there a quick command that will help with this situation? A bash script would be good enough, too.

EDIT:

By subfolders, I meant only the first-level subfolders, because I don't want to remove second-level or third-level subfolders, which might have names without the string, of first-level subfolders with the string.

  • Try find . -type d -not -name "*substring*". If that turns up the right subfolders, add the -delete flag. Does that work? – Jos Jul 16 '16 at 9:58
  • Some nspiration could come from here: askubuntu.com/q/163960/57576 :) – andrew.46 Jul 16 '16 at 10:10
  • 2
    @Jos Please note that -delete only works for empty directories. If you need to delete the directory and its content, you must use -exec rm -rf {} \; instead. – Byte Commander Jul 16 '16 at 10:15
  • You are correct @ByteCommander. Let's turn this into an answer. – Jos Jul 16 '16 at 10:28
9

Let us say you want to start find in the current directory, and restrict it to the first level of subdirectories:

find . -maxdepth 1

The find command has a useful flag -not (or !) which negates the following test. So in order to find a name which does not contain a substring, add

-not -name "*substring*"

IMPORTANT: you will want to exclude the current directory itself as well. Otherwise, the whole current directory would be deleted.

-not -name "."

Then you want to test for directories only:

-type d

And, if everything looks good, you want to delete these directories:

-exec rm -rf {} \;

which says "for all found directories, execute this command". The {} is a placeholder for the name of the directory (including the full path, so it works on the correct one). \; indicates the end of the command to be executed.

Summarizing:

find . -maxdepth 1 -not -name "*substring*" -not -name "." -type d -exec rm -rf {} \;

should work. But first, try it out without the -exec part.

|improve this answer|||||
  • 1
    @PyGeek03 I did. – Jos Jul 16 '16 at 10:55
  • 1
    Well, 1 more thing with this command though: the output without -exec rm -rf {} \; part also include . , so when I run the whole command, the content of the whole directory got deleted! (I've made a backup, as this is a test anyway). Is there any way to avoid this? – PyGeek03 Jul 16 '16 at 11:03
  • 1
    Good point. Will add that as well. – Jos Jul 16 '16 at 11:07
  • Use a plus + in place of the escaped semicolon. The find will run the rm command with multiple arguments, rather like using xargs would. – Jonathan Leffler Jul 16 '16 at 13:56
4

The bash shell's extended glob operators can do pattern negation e.g. given

$ tree .
.
├── subdir
│   ├── other file
│   └── somefile
├── subdirbar
│   ├── other file
│   └── somefile
├── subdirbaz
│   ├── other file
│   └── somefile
└── subdirfoo
    ├── other file
    └── somefile

4 directories, 8 files

then if extended globbing is enabled (shopt -s extglob)

$ rm -rf !(*foo*)

removes (recursively) all top-level directories not containing string foo, leaving

$ tree
.
└── subdirfoo
    ├── other file
    └── somefile

1 directory, 2 files

However, this would also delete any files with names not containing foo in the top level. AFAIK bash extended globs can't distinguish files from directories - but zsh provides glob qualifiers, which allow that e.g. given

 % tree
.
├── foofile
├── other file
├── somefile
├── subdir
│   ├── other file
│   └── somefile
├── subdirbar
│   ├── other file
│   └── somefile
├── subdirbaz
│   ├── other file
│   └── somefile
└── subdirfoo
    ├── other file
    └── somefile

4 directories, 11 files

then in zsh

 % setopt EXTENDED_GLOB

 % ls -d (^(*foo*))   
other file  somefile  subdir  subdirbar  subdirbaz

whereas adding the (/) directory qualifier

 % ls -d (^(*foo*))(/)
subdir  subdirbar  subdirbaz

so

 % rm -rf (^(*foo*))(/)

removes only directories whose names do not include string foo, leaving plain files intact.

 % tree
.
├── foofile
├── other file
├── somefile
└── subdirfoo
    ├── other file
    └── somefile

1 directory, 5 files
|improve this answer|||||
  • Thanks for giving me a good reason to give zsh a try :))) Turns out it is much more advanced than bash, which is a good thing :D – PyGeek03 Jul 16 '16 at 19:03

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.