Exclude from * in command line

For Bash there is a shell option called extglob that is deactivated by default to keep compatibility with standard shell syntax. Extglob complements the syntax by additional operators like !(), ?(), @() and some more.

To switch extglob on, type shopt -s extglob. To keep it switched on for the current user type echo 'shopt -s extglob' >> ~/.bashrc.

For the rm example: With extglob you can use

rm -rf !(one|two|three)

I've built except exactly for that (sry that I wasn't able to add documentation yet).

I you have a folder like the following:

$ ls
a b c d

Typing except b d will give you a and c

$ except b d
ac

Now you can pipe that to rm.

except b d | xargs -0 rm

This may be hard to remember, so why not just build a script on top of except, called rm-except:

#!/usr/bin/env bash

except "$@" | xargs -0 rm

Similarly easy is ls-except:

#!/usr/bin/env bash

except "$@" | xargs -0 ls

As an alternative to extglob (though that is a very good answer and everyone should have shopt -s extglob globstar in their .bashrc), you can use the $GLOBIGNORE global variable. Let's say you want to get every file except 'foo.txt' and 'bar baz.txt':

GLOBIGNORE=foo.txt:'bar baz.txt'

...however, this will turn the shell option dotglob on, which means that * will match files beginning with a dot (which are normally hidden). So you actually need two commands:

GLOBIGNORE=foo.txt:'bar baz.txt'
shopt -u dotglob

Since this is a global variable, it will affect every glob you use until $GLOBSTAR is unset either by logging out or with

GLOBIGNORE=

It will also only work on the literal strings passed to the variable. You can see what I mean by setting $GLOBIGNORE and looking at the difference between these commands:

printf '%s\n' *
printf '%s\n' ./*