11

I want to remove all files in a directory while leaving just some specified files, they don't have anything in common by name. How could I achieve that?

For example, the file names I want to keep are:

file_1.png, another_file.jpg, some_music.mp3

14

If you are using bash:

shopt -s extglob
rm -- !(file1|file2|file3)

The first line just activates extended pattern matching, and after that we use one of them:

!(pattern-list) matches anything except one of the given patterns

and the pattern-list is a list of one or more patterns separated by a |.


Or with zsh

setopt extendedglob
rm -- ^(file1|file2)

Or, more portable, using find:

find . -maxdepth 1 ! -name 'file1' ! -name 'file2' -type f -exec rm -v {} +
  • 3
    It might be useful to include how to return the optional shell behaviour back (shopt -u extglob). – geedoubleya Nov 7 '14 at 14:46
7

Make a subdirectory tmp, move all all the files that you want to keep to that directory and do a rm -f * afterwards. That will not affect the tmp directory.

After that just do:

mv tmp/* .
rmdir tmp

(Assuming none of the files you moved starts with a dot).

This is one of the few cases where it makes sense to use the mouse and a file browser like Nautilus where you can actually can Ctrl-click the few files you want to keep and then use the menu: Edit -> Invert Selection to get the other files and then delete those.

  • the moving files to tmp directory sounds nice. Anyway, I have to do this with command line. Thanks! – AGamePlayer Nov 7 '14 at 9:46
  • 3
    @AwQiruiGuo The tmp has the advantage that it first seperates the files into two sets, before irrevocably removing the ones you don't want. So you can run an ls to see if you didn't miss any of the files you want to preserve. – Anthon Nov 7 '14 at 10:09
2

Add the names of the files you want to keep to a text file, one per line. This file should not be in the directory or you should add its name too.

If the name of your directory is foo and the name of the file is bar, do the following.

find foo -type f -print0|grep -vzZ -f bar|xargs -0 rm

This does the following:

  1. find -type f searches for the names of all files in foo and its subdirectories and prints them to STDOUT. With option -print0 it delimits the filenames with NUL bytes.
  2. grep -v -f bar excludes all filenames that match a line in bar. With option -z it treats its input as a set of lines delimited by NUL byte, with option -Z it delimts its output with a NUL byte.
  3. xargs rm calls rm repeatedly to delete the files. With option -0 it treats its input as a set of lines delimited by NUL byte.

This works with filenames containing white space.

  • This won't work as filenames can have newlines in them. – Anthon Nov 7 '14 at 9:52
  • @Anthon changed the options of find, grep, xargs to cope with filenames containing whitespace, including newline – Mathias Weidner Nov 7 '14 at 10:27
  • If you want to use find I would rather do find . -maxdepth 1 ! -name 'file1' ! -name 'file2' -type f -exec rm -vi {} + – jimmij Nov 7 '14 at 10:51

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.