26

I have a lot of files and subfolders in a specific folder and I want to delete all of them; however, I wanted to keep files X, Y, and Z.

Is there a way I can do something like:

rm * | but NOT grep | X or Y or Z

0

8 Answers 8

27

Instead of using rm, it may be easier to use find. A command like this would delete everything except a file named exactly 'file'

find . \! -name 'file' -delete

Many versions of should be able to support globbing and regular expression matching.

You could also pipe the output of find to rm as well

find . \! -name '*pattern*' -print0 | xargs --null rm 
3
  • 3
    And this lets you see exactly what files will be deleted by removing the -delete as well as what files will be saved by removing -delete and the \!. Feb 11, 2011 at 0:51
  • 1
    careful, find is recursive! so all files subdirectories not matching 'pattern' will get deleted as well Jun 23, 2014 at 22:07
  • The way via xargs needs -depth, to prevent the attempt of deleting directories before files included in them. However, I think rm will not remove directories either. Sep 13, 2017 at 21:24
9

Using zsh, with setopt EXTENDED_GLOB, using the ~ operator (except)

rm -- *~(x|y|z)

or ^ operator (negation):

rm -- ^(x|y|z)

But, you should probably instead move the files elsewhere, then delete everything. It's far safer in terms of finger slips, such as hitting enter too soon.

0
7

Later versions of bash have the extglob shell option that gives you a syntax for doing what you want (check your man page under "Pathname Expansion" to see if your installed version has it):

$ shopt -s extglob  # turn on extended globbing
$ rm !(X|Y|Z)

To test, I suggest you first replace rm with echo to see if the list of files to be deleted is what you expect.

7
ls -1 | grep -v "^[XYZ]$" # | xargs rm -r

Attention: Run the command and if the files to be deleted are the right ones, run it again and delete the hash character "#".

If the filenames are more complicated then that, do

ls -1 | egrep -v "^file1$|^filename2$|^f1le$" # | xargs rm -r

Again, first look at the results then remove the hash sign.

This version - as suggested in the comments - saves some characters and looks a bit clearer.

ls -1 | egrep -v "^(file1|filename2|f1le)$" # | xargs rm -r
2
  • A suggested edit by ddeimeke is to change the egrep argument to "^(file1|filename2|f1le)$" Feb 11, 2011 at 22:37
  • If you have three files a, b and a b the first program will delete - if you put ab into the grep part: "^[ab]$" a and b but not a b, just opposed to your intentions. The second command will do the same. And the third too. You shouldn't use 'ls' in scripts, with very, very rare exceptions. Just don't do it. Mar 12, 2011 at 2:17
4

Move the files you want to keep away. Go up one level, delete the folder. Re-create the folder and move those files back.

1
  • I wonder why the minus?.. It's actually both easier and safer than anything proposed by others: you simply can't shoot yourself in a foot by hitting some special char in the filename.
    – alex
    Feb 11, 2011 at 8:34
2

It will delete all (including hidden) except selected files/folders in the CURRENT directory.

find . -maxdepth 1 ! -name "file1.php" ! -name "file2.js" ! -name "dir1" ! -name "dir2" ! -name . -exec rm -r {} \;
0

This is what I set up as a .sh script for an OSX/MacOS logout hook, works well enough.

#! /bin/bash
for dir in /PathToFolder/*
 do
    if [ ! "$dir" = "/PathToFolder/FolderToKeep1" ] && [ ! "$dir" = "/PathToFolder/FolderToKeep2" ] && [ ! "$dir" = "/PathToFolder/FolderToKeep3" ] && [ ! "$dir" = "/PathToFolder/FolderToKeep4" ] && [ ! "$dir" = "/PathToFolder/FolderToKeep5" ] && [ ! "$dir" = "/PathToFolder/FolderToKeep6" ] ; then 
      echo ${dir}
      sudo rm -R $dir user
      dscl . -delete $dir
    fi 
  done

exit 0
0

With GNU-find you can use the -delete switch, which removes directories, if empty, too:

 find tmp -not -name X -not -name Y -not -name Z -delete 
5
  • If X, Y or Z is a directory, this will delete files inside them as well. find tmp -maxdepth 1 -not -name X -not -name Y -not -name Z -delete is one way to work around this. Mar 12, 2011 at 1:00
  • Since folders under Unix are files (anything is a file), that's maybe what is intended. The question explicitly says: "I have a lot of files and folders in a specific folder and I want to delete all of them; " - and if you like to exclude Y, maybe you know whether Y is a directory or not. Mar 12, 2011 at 1:37
  • That will end up deleting tmp as well if there were no X/Y/Z files in it. Note ! as the standard equivalent of GNU's -not (-delete is also a GNU extension) Sep 13, 2017 at 12:32
  • @StéphaneChazelas: From the question, deleting folders (all of them) is what was asked for. Since the bang needs masking, I prefer typing -not. Might depend on the german keyboard layout, where Backslash needs Alt-Gr. Sep 13, 2017 at 21:20
  • originally the question's title didn't mention deleting subfolders, only the question's description did. to only delete all files that aren't excluded but not subfolders use -type f like this find tmp -not -name X -not -name Y -not -name Z -type f -delete Feb 24, 2023 at 23:16

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .