Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

In the UNIX shell how should I remove everything from a directory except a certain number of files?

For example, how would I remove every file and folder but the folder foo?

If there's also an equivalent for DOS shell, what is it?

share|improve this question

7 Answers 7

First, test with:

find path-to-folder -maxdepth 1 ! -iname 'excludeme' -and -type f -exec ls "{}" \;

Then:

find path-to-folder -maxdepth 1 ! -iname 'excludeme' -and -type f -exec rm "{}" \;

-maxdepth 1 means to look in the current folder only (and it needs to be the first option)

! -iname (or -name) says to not include anything that matches what you want to stay.

-type f makes sure you don't get any directories in the current folder.

share|improve this answer
    
good answer, some versions also have a -delete flag –  Andy Aug 17 '09 at 20:36
1  
For some reason, I'm always afraid of using it. I feel so much more comfortable with ls and then replacing the ls with rm once I verify the output, even though I could do the exact same thing with -print and -delete. –  Dave Aug 17 '09 at 21:44
find . -maxdepth 1 -path ./folder -prune -o -exec rm -rf '{}' ';'

Be sure to double check it first with some test data!

share|improve this answer

To match everything but something is generally referred to as an inverse match. To expand filename is the shell itself you use globbing. If you are going to call a program outside of the shell you would probably want find as people have mentioned.

You can do an inverse match with a newer bash if you enable extended globbing. For example, to match everything that doesn't have foo or bar in the name:

shopt -s extglob
echo !(*foo*|*bar*)

Or just everything that doesn't have foo:

shopt -s extglob
echo !(*foo*)
share|improve this answer

If there are no open files in foo, you could just move it, delete everything else, and move it back.

I don't think there is an easy "negative" match in bash's globbing, though you might be able to manufacture one using find's regex and iregex matches.

edit: just a thought...

ls | grep -v ^./foo$

will list all the files except foo, so you could use

ls | grep -v ^./foo$ | xargs rm

though that will have problems if any of the file/directory names contain spaces.
You can alter the regex passed to grep as needed if you want to match a pattern rather than a single file/directory, or pass through grep more times:

ls | grep -v ^./foo$ | grep -v ^./bar$ | xargs rm -rf

Be very careful to test the output of the find+grep sequence before adding the xargs rm -rf step.

edit again: ls does the same as find . -maxdepth 1 and is probably simpler.

share|improve this answer
    
It is OK also if the files are open and you are moving them in the same filesystem. It is not OK if a program tries to open the moved files during the change(rename, delete, rename back). If the files are open, the file descriptor point towards the inode - so it does not matter the name. –  Mircea Vutcovici Jan 27 '10 at 13:56

If it were a small list, I would do something like:

ls -1|egrep -v "(file2|file8)"|xargs rm -rf

Which gets the list of files, then greps for the regex excluding the files you want to keep, and removes everything else.

You should DEFINITELY test this by leaving off the xargs part to verify you have the right set of files.

share|improve this answer

There is also a more casual solution, which is:

  1. mkdir ../temp
  2. ln precious1 precious2 precious3 ../temp/ (or mv instead of ln)
  3. rm -rf *
  4. ln ../temp/* .
  5. rmdir ../temp

The disadvantage is that your precious files are momentarily displaced. This solution would not be suitable if your precious files must be available the entire time.

However, this method has the advantage that you can verify after step 2 that your precious files are safe. This is particularly significant if there are hidden files or subdirectories involved, which would raise the chances of making a mistake with the other methods suggested here.

share|improve this answer

Version for Windows cmd.exe

Print the names of all directories in C:\ except those called 'foo':

@echo off
FOR /D %A IN (C:\*) DO (IF /I NOT "%A"=="C:\foo" (echo %A))

Print the names of all files in C:\ except those called 'foo' (ignoring file extensions):

@echo off
FOR %A IN (C:\*.*) DO (IF /I NOT "%~nA"=="foo" (echo %A))

Print the names of all files in C:\ except those with the extension 'bar':

@echo off
FOR %A IN (C:\*.*) DO (IF /I NOT "%~xA"==".bar" (echo %A))

Print the names of all files in C:\ except those called 'foo.bar':

@echo off
FOR %A IN (C:\*) DO (IF /I NOT "%~nxA"=="foo.bar" (echo %A))

Change the 'echo' command to whatever it is that you want to do - e.g. 'del'

All of the above are case INsensitive - remove '/I' to change this.

Type 'for /?' for more details on FOR's %~ syntax.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.