I need to delete all files in a directory, but exclude some of them. For example, in a directory with the files a b c ... z
, I need to delete all except for u
and p
. Is there an easy way to do this?
17 Answers
What I do in those cases is to type
rm *
Then I press Ctrl+X,* to expand *
into all visible file names.
Then I can just remove the two files I like to keep from the list and finally execute the command line.
-
26I guess this works only as long as the list of files which
*
expands too isn't getting too long. :-} Commented Jan 8, 2013 at 16:57 -
11
-
2@SantoshKumar: That doesn't make sense to me. The expansion will always work, it doesn't depend on what command you want to use afterwards. Commented Jan 17, 2013 at 18:18
-
2@OliverSalzburg Sorry, the combination is little bit confusing. I think you should write like
Ctrl
+Shift
+x
+*
Commented Jan 18, 2013 at 5:22 -
3
To rm
all but u,p
in bash just type:
rm !(u|p)
This requires the following option to be set:
shopt -s extglob
See more: glob - Greg's Wiki
-
1
-
19You need to
shopt -s extglob
, @Ashot. Also, it's just files, not directories, which is why I've removed the-rf
options in your command.– slhckCommented Jan 8, 2013 at 13:07 -
4If you need to exclude one file of a selection of files, try this:
rm !(index).html
. This will delete all files ending in ".html" with the exception of "index.html". Commented Jul 24, 2015 at 21:46 -
1
You can use find
find . ! -name u ! -name p -maxdepth 1 -type f -delete
!
negates the next expression-name
specifies a filename-maxdepth 1
will make find process the specified directory only (find
by default traverses directories)-type f
will process only files (and not for example directories)-delete
will delete the files
You can then tune the conditions looking at the man page of find
Update
- Keep in mind that the order of the elements of the expressions is significant (see the documentation)
Test your command first by using
-print
instead of-delete
find . ! -name u ! -name p -maxdepth 1 -type f -print
-
8order of predicates is critical here. If one put
-delete
just after.
it will be disaster (will delete all files in CWD) Commented Jan 8, 2013 at 17:34 -
This could be written more compactly as
find . -maxdepth 1 -type f -name '[^up]' -delete
– kojiroCommented Jan 8, 2013 at 18:09 -
5@kojiro yes but only for files that are just one letter. With more complex names the regex could be a mess.– MatteoCommented Jan 8, 2013 at 20:15
-
3
find
is my best friend, especially when there are too many files to glob Commented Jan 9, 2013 at 22:36 -
1
Simple:
mv
the files you want in a upper directory, rm
the directory and then mv
them back.
-
13Offcourse, mv them to a directory higher. Try not to mv them to a subdirectory you are deleting...– KonerakCommented Jan 8, 2013 at 19:21
-
1
-
12This will overwrite files with the same name in the destination directory– MatteoCommented Jan 9, 2013 at 9:29
-
11I am downvoting this because while it can be handy, it also is non-atomic and effectively removes all files from the directory during a short period of time; this would not be acceptable if, for instance, the files are being shared on the network. Commented Jan 10, 2013 at 12:19
-
1Also you'd need write access on the parent directory, which you likely not have on a shared web server. Commented Apr 4, 2020 at 20:57
Somewhat similar to this answer but no special options are needed, as far as I know the following is "ancient" functionality supported by any (vaguely) /bin/sh resembling shell (e.g. bash, zsh, ksh, etc)
rm [^up]
-
2This works for the 1-char filenames. For longer names, sparkie's answer is better. Commented Jan 8, 2013 at 15:31
-
3What would be wrong with
rm [^up]*
? I do similar things rather often.– userCommented Jan 8, 2013 at 15:33 -
3@MichaelKjörling - this would delete all files beginning with either u or p, not just those with the names u and p. I think the OP (@Ashot) meant the a-z and u,p,etc. symbolically and not literally. Commented Jan 9, 2013 at 9:07
-
4@HobbesofCalvin That would delete all files not beginning with u or p, not those beginning with them.– rjmunroCommented Jan 9, 2013 at 10:48
Doing it without find:
ls | grep -v '(u|p)' | xargs rm
(Edit: "u" and "v", as in other places here, are being used as generic versions of entire regexes. Obviously you'll want to be careful to anchor your regexes to avoid matching too many things.)
You're definitely going to want a script if you're going to be doing much of this, as others have suggested.
-
1grep will not handle extended regexpt by default: either use
-E
oregrep
– MatteoCommented Jan 9, 2013 at 9:28 -
2
-
1@Matteo No it won't. The grep isn't grepping the files, it's grepping the output of the ls command. You're thinking of something like
grep -L (u|p)' * | xargs rm
where-L
means list filenames not containing a match.– rjmunroCommented Jan 9, 2013 at 10:52 -
5Oh, you mean any file who's name contains
u
orp
, not any file containing au
or ap
. That is correct. You can fix by usingegrep -v '^(u|p)$'
– rjmunroCommented Jan 9, 2013 at 12:40 -
1Here is remove everything except these matches!
ls | grep -v 'vuze\|progs' | xargs rm -rf
– NickCommented Sep 25, 2014 at 12:32
In zsh:
setopt extended_glob # probably in your .zshrc
then
rm ^(u|p)
or
rm *~(u|p)
The second will work even if you have ^
in $histchars
for history substitution, and of course you can put an arbitrary glob before the ~
.
GLOBIGNORE takes a colon-separated list
GLOBIGNORE=u:p
rm *
-
14This does not work on my shell (GNU bash 4.1.5(1)). Be sure to test it first with something a little less harmful than
rm
or in a testing directory!– userCommented Jan 8, 2013 at 20:09
Back in the floppy era I had a dos executable called "Except" that would move things out of the current directory temporarially and execute a command, so you could say:
except *.txt del *.*
to delete everything but your text files.
This would be a pretty trivial thing to implement as a shell script and if this is the kind of thing you are likely to do more than twice it seems like it would be a good idea.
-
2It reminded me the same thing. But temporarily moving out of folder may not be a good idea in the era of multitasking :) Commented Jan 9, 2013 at 9:18
find . -maxdepth 1 ! -name "u" ! -name "p" -type f -exec rm -rf {} \;
This will delete all files except u and p in unix
For those preferring to specify arbitrary complex exclude patterns (spanning all affected filenames) in a full blown regexp emacs, posix-awk or posix-extended style (see find man page) I would recommend this one. It excludes u
and p
in current dir in this example. This may be handy for scripts.
find -regextype posix-awk ! -regex './(u|p)' -print0 | xargs -0 rm -rf
-
You need to specify a directory before the expression (`find . -regextype ...).– MatteoCommented Jan 9, 2013 at 9:25
-
-
no - my find version (debian squeeze) does definitively not require an explicit directory before the expression if the current directory should be used– sparkieCommented Jan 9, 2013 at 9:44
-
-
@sparkie: not defining the directory (first parameter) for
find
is a GNU extension offind
command. The same applies for-regextype
option. In addition, your command will delete files in subdirectories, too, whereas the original question clearly asked about files in a directory. Commented Sep 4, 2015 at 5:28
Yet another:
for FILE in ./*; do if [[ $FILE != ./u* ]] || [[ $FILE != ./p* ]];then rm $FILE; fi; done;
It's kind of lengthy and I don't know if you could easily make it into an function that could easily accommodate and arbitrary number of arguments, but it works well.
And it's pure bash goodness.
Here's another variant. You can type:
rm -i *
or:
rm --interactive *
So rm
will ask you to confirm deleting of each file.
Use:
find . -type f ! -name 'u' ! -name 'p' ! -name '*.ext' -delete
find . -type d ! -name 'u' ! -name 'p' ! -name '*.ext' -delete
in order to delete all files including directories, except u, p and .ext files.
I always use:
rm [a-o,q-t,v-z]*
This will allow you to define how granular you want to make it. So if you want to delete a through o and Z files you can use:
rm [a-o,z]*
Yet another version using xargs
:
ls -1 | grep -v do_not_delete | xargs -I files rm "files"
Note that xargs -I
is needed to handle filenames including spaces correctly.
A simple way that is hard to mess up: let's say you want to delete everything except *.pdf:
mkdir tmp
mv *.pdf tmp
rm *
mv tmp/* .
rm -r tmp
-
1It would be better to use
rmdir tmp
in that last line instead ofrm -r tmp
-- that way, if previous command failed for any reason or was mistyped, you won't lose all your data. Commented Jun 4, 2023 at 23:17
shopt -s dotglob
before runningrm (...)
.