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?
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
-
Some of these may need to be adapted depending on whether you have folders and what you want to do with them.– KevinJun 5, 2013 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.– KevinJun 6, 2013 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. Jan 17, 2014 at 8:56
-
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
– NtwobikeJun 8, 2018 at 7: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 {} \;
there are many ways could do it. but the most simple way would be (bash):
shopt -s extglob
rm !(*.txt)
-
-
shopt -s extglob
is powerful. tecmint.com/… provides good examples. To delete all except certain extensions,rm -v !(*.zip|*.odt)
works.– Martin_WNov 23, 2020 at 6:52
You can use inverted grep and xargs
ls | grep -v .txt$| xargs rm
-
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.) Jul 21, 2016 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. Nov 19, 2018 at 3:31
One solution without find
:
mv dir/*.txt otherdir/
rm -r dir
mv otherdir dir
This should work on all kind of shells.
-
I think this definitely has its place as it's the answer that an occasional user is most likely to remember in the long term.– MehmetMar 11, 2022 at 7: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
-
1Welcome 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. Jul 21, 2016 at 15:59 -
-
-
This 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 9, 2022 at 1:23
This works also to remove all hidden (dot) files and folders except the stated one (.mydir
):
rm -rf $(ls -aI ".mydir")
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.
ls |awk '!/\.txt$/{print "rm -rvf "$1}'| sh
-
Why 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. Mar 9, 2022 at 8:31
ls
before actually attempting to callrm
.