Reputation: 4643
This removes all files that end with .a
or .b
$ ls *.a
a.a b.a c.a
$ ls *.b
a.b b.b c.b
$ rm *.a *.b
How would I do the opposite and remove all files that end with *.*
except the ones that end with *.a
and *.b
?
Upvotes: 0
Views: 1578
Reputation: 785058
You can enable extended glob in bash:
shopt -s extglob
Then you can use:
rm *.!(a|b)
To remove all files that end with *.*
except the ones that end with *.a
OR *.b
Update: (Thanks to @mklement0) Here is a way to localize setting extglob
(without altering the global state) by doing this in a subshell
and using an intermediate variable:
(shopt -s extglob; glob='*.!(a|b)'; rm $glob)
Upvotes: 1
Reputation: 437408
The linked answer has useful info, though the question is somewhat ambiguous and the answers use differing interpretations.
The simplest approach in your case is probably (a streamlined version of https://stackoverflow.com/a/10448940/45375):
(GLOBIGNORE='*.a:*.b'; rm *.*)
(...)
) to localize setting the GLOBIGNORE
variable.GLOBIGNORE
must be :
-separated.The appeal of this approach is that you can use a single subshell without changing global state.
By contrast, getting away with a single subshell with shopt -s extglob
requires a bit of trickery:
(shopt -s extglob; glob='*.!(a|b)'; echo $glob)
Note the mandatory use of an intermediate variable, without which the command would break (because a literal glob would be expanded BEFORE executing the commands, at which point the extended globbing syntax is not yet recognized).
Caveat: Using GLOBIGNORE
has an unexpected side effect (bug?):
If GLOBIGNORE
is set - to whatever value - pathname expansion of *
and *.*
behaves as if shell option dotglob
were in effect - even if it isn't.
In other words: If GLOBIGNORE
is set, hidden files not explicitly exempted by a pattern in GLOBIGNORE
are always matched by *
and *.*
.
dotglob
is OFF by default, causing *
NOT to include hidden files (if GLOBIGNORE
is not set, which is true by default).
If you also wanted to exclude hidden files while using GLOBIGNORE
, add the following pattern: .*
; applied to the question, you'd get:
(GLOBIGNORE='*.a:*.b:.*'; rm *.*)
By contrast, using extended globbing after turning on the extglob
shell option DOES respect the dotglob
option.
Upvotes: 2
Reputation: 70263
Sometimes it's better to not insist on solving a problem a certain way. And for the general problem of "acting on certain files to be determined in some tricky way", find
is probably the best all-around tool you'll find.
find . -type f -maxdepth 1 ! -name \*.[ab] -delete
Omit the -maxdepth 1
if you want to recurse into subdirectories.
Upvotes: 1
Reputation: 4550
There are some shells that are capable of this (I think?), however, bash
is not by default. If you are running bash
on Cygwin, you can do this:
rm $(ls -1 | grep -v '.*\.a' | grep -v '.*\.b')
ls -1
(that's a one) list all files in current directory one per line.grep -v '.*\.a'
return all matches that don't end in .agrep -v '.*\.b'
return all matches that don't end in .bUpvotes: 0