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.
 
                