There are 2 ways to read this question, and the existing answers cover both interpretations: EITHER: (a) preserve files with the specified names directly located in the target directory and - as rm -r implies - delete everything else, including subdirectories - even if they contain files with the specified names; OR: (b) traverse the entire subtree of the target directory and, in each directory, delete all files except those with the names listed.
– mklement0Jun 5 '14 at 4:31

@nic, @Dennis: The syntax error suggests that something OTHER than bash was used, e.g., dash, where extglob is not supported. However, in an interactivebash shell, the command will ALSO not work as stated, though for different reasons. The short of it: execute shopt -s extglob BY ITSELF; ONCE SUCCESSFULLY ENABLED (verify with shopt extglob), execute rm -rf !(README|LICENSE). (While extglob is not yet enabled, !(...) is evaluated by history expansion BEFORE the commands are executed; since this likely fails, NEITHER command is executed, and extglob is never turned on.)
– mklement0Jun 5 '14 at 3:45

@nic, @Dennis, @mklement0: I had the same issue with "syntax error near unexpected token (" when executing the command within a .sh file (#! /bin/bash) but not when I was running it in a command line interface. It turned out that in addition to the shopt -s extglob run in the command line interface, I had to rerun it inside my script file. This solved the problem for me.
– AhresseJan 6 '17 at 11:49

It will create a backup directory /tmp_backup (you've got root privileges, right?), move files you listed to that directory, delete recursively everything in current directory (you know that you're in the right directory, do you?), move back to current directory everything from /tmp_backup and finally, delete /tmp_backup.

I choose the backup directory to be in root, because if you're trying to delete everything recursively from root, your system will have big problems.

Surely there are more elegant ways to do this, but this one is pretty straightforward.

If you want to simply delete everything in the current directory other than the excluded criteria: find . -maxdepth 1 | grep -v "exclude these" | xargs rm -r works much faster as it doesn't need to drill down in to directories unnecessarily.
– billynoahJan 17 '14 at 19:25

Re find pipeline: efficiency issues aside (3 commands are used for what find could do alone), this will not work as intended with filenames with embedded spaces and will potentially delete the wrong files.
– mklement0Jun 5 '14 at 4:18

It is more complicate because you have to take care of permissions after you copy them back.
– RomulusOct 1 '14 at 15:59

2

@RemusAvram You can use appropriate switches with cp or rsync to preserve permissions. Anyways, this is just an alternate method (given as a suggestion) that has its place here, as an answer to the OP.
– gniourf_gniourfOct 1 '14 at 16:03

This may not be appropriate in many situations wherein the files to be retained are actively being used by other processes. It is also cumbersome of the files to be removed are just a small subset and a large number of files are involved.
– codeforesterJan 19 at 18:41

@codeforester: sure. But there are situations where it's appropriate, though... in fact I don't really see the point of your comment :).
– gniourf_gniourfJan 19 at 21:34

A little late for the OP, but hopefully useful for anyone who gets here much later by google...

I found the answer by @awi and comment on -delete by @Jamie Bullock really useful. A simple utility so you can do this in different directories ignoring different file names/types each time with minimal typing:

Does NOT work for files. rm !(myfile.txt) removes all including myfile.txt
– khaverimFeb 10 '17 at 23:20

should execute shopt -s extglob first as @pl1nk pointed out
– zhuguoweiJun 21 '17 at 1:58

Yes, it's very important to activate extended globbing (extglob) in bash, I am doing this in a daily routine basis, over a few Macs in labs: shopt -s extglob and then cd /Users/alumno/ and finally rm -rf !(Applications|Virtualbox*VMs|Downloads|Documents|Desktop|Public|Library) Read about extended globbing here
– juanfalApr 3 at 14:21

The existing answer by Leonardo Hermoso does this with fewer bugs. This will fail for file names with spaces; using an array elegantly solves that particular problem (and also less crucially avoids using uppercase for his private variables, which is generally to be avoided).
– tripleeeAug 22 at 11:56

The regex comparison is incorrect -- it will preserve any file whose name is a substring of one of the protected files (though the surrounding spaces somewhat mitigate that; but file names can contain spaces). You should collect an array of all files, then subtract the files you want to exclude from that array, then remove the remaining files.
– tripleeeAug 22 at 11:57

This may not be appropriate in many situations wherein the files to be retained are actively being used by other processes. It is also cumbersome of the files to be removed are just a small subset and a large number of files are involved.
– codeforesterJan 19 at 18:39