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
rmwould 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 rmwill give errorrm: missing operandwhen all files in the folder have*.txtextension (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 rmwould 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 extglobis 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 *.txtwill work only if there's exactly one.txtfile. If there is none,grepwill use*.txtas the pattern; if there's more than one, it will search for the first filename inside all of the other.txtfiles, 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 withtxtregardless of the dot. Becausegreptakes regular expression as parameter. So filesa.txtandaatxtanda-txtwill 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-vflag 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
-Ioption could be useful, but see why not parse ls. And what does the| xargsachieve? Without any arguments, it just runsechoon 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
lsbefore actually attempting to callrm. – chepner Jun 05 '13 at 15:58