I know how to delete all txt
file under current directory by rm *.txt
.
Does anyone know how to delete all files in current directory EXCEPT txt
file?

- 829,060

- 1,839
- 3
- 15
- 9
9 Answers
You can use find
:
find . -type f ! -name '*.txt' -delete
Or bash's extended globbing features:
shopt -s extglob
rm *.!(txt)
Or in zsh:
setopt extendedglob
rm *~*.txt(.)
# || ^^^ Only plain files
# ||^^^^^ files ending in ".txt"
# | \Except
# \Everything

- 1,715
- 2
- 15
- 35

- 40,767
-
Some of these may need to be adapted depending on whether you have folders and what you want to do with them. – Kevin Jun 05 '13 at 15:58
-
3
-
1@LauriRanta depends on what's in the folder, which we haven't gotten an answer to. It's fine as is if all the files have extensions, and
rm
would choke if there were folders. – Kevin Jun 06 '13 at 13:22 -
I've got an issue with the brackets. When I use the globbing style in a bash script, it complains about a syntax error and the parentheses. However doing it from the CLI works. – CMCDragonkai Jan 17 '14 at 08:56
-
-
@Ntwobike
xargs rm
will give errorrm: missing operand
when all files in the folder have*.txt
extension (i.e. non-txt files were not found). This makes it impossible to use your variant in.sh
-files when there are other commands after the failing one. – izogfif Jun 07 '18 at 04:45 -
1@izogfif check this
find . -type f ! -name "*.txt" | xargs -r rm
would work in GNU\xargs. BSD and UNIX xargs command may not have -r you have to check your localman xargs
– Ntwobike Jun 08 '18 at 07:52 -
-
If you just want to delete all files except '*.txt' then you can use the following command:
$ find . -type f ! -name "*.txt" -exec rm -rf {} \;
but if you also want to delete directories along with the files then you can use this:
$ find . ! -name "*.txt" -exec rm -r {} \;

- 363
there are many ways could do it. but the most simple way would be (bash):
shopt -s extglob
rm !(*.txt)

- 768
-
-
shopt -s extglob
is powerful. https://www.tecmint.com/delete-all-files-in-directory-except-one-few-file-extensions/ provides good examples. To delete all except certain extensions,rm -v !(*.zip|*.odt)
works. – Martin_W Nov 23 '20 at 06:52
You can use inverted grep and xargs
ls | grep -v .txt$| xargs rm

- 211
-
1
-
@phillipsk
grep -v *.txt
will work only if there's exactly one.txt
file. If there is none,grep
will use*.txt
as the pattern; if there's more than one, it will search for the first filename inside all of the other.txt
files, ignoring the output fromls
. (Exact results may depend on the shell's glob options.) – JigglyNaga Jul 21 '16 at 16:11 -
1
.txt$
will match strings ending withtxt
regardless of the dot. Becausegrep
takes regular expression as parameter. So filesa.txt
andaatxt
anda-txt
will all be matched by this expression. Correct expression should bels | grep -v \\.txt$ | xargs --no-run-if-empty rm
. For curious people: If you want to play around with the expression safely use this test expressionls | grep \\.txt$ | xargs --no-run-if-empty echo
(note: there's no-v
flag andrm=>echo
). Note2: you may have noticed double backslash. One is for regex, another is for bash to escape slash. – Dimitry K Nov 19 '18 at 03:31
One solution without find
:
mv dir/*.txt otherdir/
rm -r dir
mv otherdir dir
This should work on all kind of shells.

- 21,510
-
1I think this definitely has its place as it's the answer that an occasional user is most likely to remember in the long term. – Mehmet Mar 11 '22 at 07:01
Simply do:
rm $(ls -I "*.txt" )
#Deletes file type except *.txt
Likewise, if need to delete "except one or more file type", do:
rm $(ls -I "*.txt" -I "*.pdf" )
#Deletes file types except *.txt & *.pdf

- 479
-
2Welcome to StackExchange! The
-I
option could be useful, but see why not parse ls. And what does the| xargs
achieve? Without any arguments, it just runsecho
on all of its input. – JigglyNaga Jul 21 '16 at 15:59 -
-
-
1This won't work if there are spaces in the file names. xargs would have been better. So
ls -1I '*.txt' | xargs -d'\n' rm
– CR. Mar 09 '22 at 01:23
This works also to remove all hidden (dot) files and folders except the stated one (.mydir
):
rm -rf $(ls -aI ".mydir")

- 111
I made a modular bash function for this, based on a compilation of findings:
rmexcept()
{
files=()
for pattern in "$@"
do
files+=(`find . -maxdepth 1 -type f -not -iname "$pattern"`)
done
# filter for duplicates only when more than one pattern provided
if (($# > 1))
then
printf "%s\n" ${files[@]} | sort | uniq -d | xargs rm
else
printf "%s\n" ${files[@]} | xargs rm
fi
}
It is designed to work with multiple pattern arguments:
rmexcept '*.tex' '*.pdf'
NOTE: The single quotes are necessary! Otherwise bash will expand the wildcard and you will have a number of inputs equal to the matching expansion, which causes every file to eventually repeat, and thus, causes the function to delete everything!
If you don't want to remember this dangerous caveat (I don't), define
rmexcept
as follows:
rmexcept()
{
files=()
for pattern in "$@"
do
files+=(`find . -maxdepth 1 -type f -not -iname "*$pattern"`)
done
# filter for duplicates only when more than one pattern provided
if (($# > 1))
then
printf "%s\n" ${files[@]} | sort | uniq -d | xargs rm
else
printf "%s\n" ${files[@]} | xargs rm
fi
}
And use without wildcards:
rmexcept .tex .pdf
NOTE: You can still make a dangerous mistake by using a prefix *
. I'll
keep thinking about how to improve this.
The way I put the find
results in an array might not be best practice.
See this thread for more
details.

- 717
ls |awk '!/\.txt$/{print "rm -rvf "$1}'| sh

- 5,211
-
1Why not parse
ls
(and what to do instead)? You’ve also got a wonderful command injection there — I hope you don’t care about any of the data on any systems on which you run similar commands, or that you don’t mind your personal data (SSH keys, GPG keys for example) being shared. – Stephen Kitt Mar 09 '22 at 08:31
ls
before actually attempting to callrm
. – chepner Jun 05 '13 at 15:58