In linux, how to delete all files EXCEPT the pattern *.txt?

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?

Asked By: Firegun

||

One solution without find:

mv dir/*.txt otherdir/
rm -r dir
mv otherdir dir

This should work on all kind of shells.

Answered By: michas

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
Answered By: Kevin

there are many ways could do it. but the most simple way would be (bash):

shopt -s extglob
rm !(*.txt)
Answered By: Kent

You can use inverted grep and xargs

ls | grep -v .txt$| xargs rm
Answered By: rjv

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 {} ;

Answered By: Sagar Rakshe

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

Answered By: Surya

This works also to remove all hidden (dot) files and folders except the stated one (.mydir):

rm -rf $(ls -aI ".mydir")
Answered By: eQ19

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 "%sn" ${files[@]} | sort | uniq -d | xargs rm
    else
        printf "%sn" ${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 "%sn" ${files[@]} | sort | uniq -d | xargs rm
    else
        printf "%sn" ${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.

Answered By: young_souvlaki
ls |awk '!/.txt$/{print "rm -rvf "$1}'| sh
Answered By: Praveen Kumar BS
Categories: Answers Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.