`[!]` (exclamation mark in brackets) wildcard in bash
Solution 1:
Citing man 7 glob
:
An expression "[!...]" matches a single character, namely any character
that is not matched by the expression obtained by removing the first '!' from it.
(Thus, "[!]a-]" matches any single character except ']', 'a' and '-'.)
Emphasis on the single character part here, []
can not be used for whole strings in Bash Pattern Matching.
bash
's Brace Expansion can be used to match strings, however there is no way (I know of) to negate them. E.g.
1.{gif,csv,mp3}
is expanded to
1.gif 1.csv 1.mp3
no matter whether these files exist.
As wchargin pointed out shopt
can be used to enable extended pattern matching operators, one of them being !()
. After running shopt -s extglob
, e.g.
1.!(gif|csv|mp3)
is expanded to
1.jpg 1.bmp 1.png
if those files exist in the current directory. For more read man bash
(section EXPANSION/Pathname Expansion) and Pathname expansion (globbing).
For what you're trying to do I'd always use find
, which offers the negation and much more, e.g. in the following way:
find . -maxdepth 1 -type f ! -name "*.gif" -a ! -name "*.csv" -a ! -name "*.mp3"
This just lists the findings, if you want to remove them just append the -delete
option to the end of the command. As always with removing actions: Always check carefully first.
find
offers a ton of useful options very well explained by man find
.
Solution 2:
I think you misunderstood the use of brackets. [abc]
matches one of the characters a
, b
or c
. [!abc]
matches one character that is not a
, b
or c
. So the commmand
rm myfile [192]
will remove myfile
and the files 1
, 9
and 2
because you have a space between myfile and the bracket. In contrast, the command
rm myfile[192]
will remove the files myfile1
, myfile9
and myfile2
.
Solution 3:
Brackets [
]
denote a character class. Any single character inside them may match in the given position. So
file[192]
matches three possible names:
file1
file2
file9
It does not match file192
, because it only deals with the single character after file
.
We can also use ranges in brackets. [0-9]
matches any single digit in the given position. For two digits, we need [0-9][0-9]
. For a digit followed by a capital letter, we could use [0-9][A-Z]
...
!
indicates negation.
file[!192]
will match files that have any single character after file
except 1
or 9
or 2
. For example it will match file7
and files
and file%
(and many others) but not file192
, because that has two extra characters.
For your use case, I suggest using find
instead of [!]
. It has a not
operator.
It's also recursive, which means it goes into all subdirectories by default. You can limit it to the current directory with -maxdepth 1
, if that's what you want. Shell wildcards (globbing) is non-recursive (although recursive globbing with **
can be enabled).
Assuming you don't want to avoid deleting symlinks, we can add -type d
to the negated tests to avoid deleting any directories. Thanks to Eliah Kagan for that suggestion.
find /path -maxdepth 1 -not \( -type d -or -iname "*.gif" -or -iname "*.csv" -or -iname "*.mp3" \)
If you see what you want, you can run the command again with -delete
find /path -maxdepth 1 -not \( -type d -or -iname "*.gif" -or -iname "*.csv" -or -iname "*.mp3" \) -delete