5

I have 10 files in current directory:

10test
1test
2test
3test
4test
5test
6test
7test
8test
9test

I want to remove all file except 2test and 3test, but I run command rm !(2test|3test) doesn't work. I get the following error:

zsh: no matches found: !(2test|3test)

3 Answers3

7

!(pattern) is ksh glob syntax, in zsh, you use ^(pattern) to negate the matching when extendedglob enabled:

setopt extendedglob
print -rl -- ^(2test|3test)

If you want to use ksh syntax, you need to enable kshglob:

setopt kshglob
print -rl -- !(2test|3test)

You can also use the and-not/except operator:

setopt extendedglob
print -rl -- *test~[23]*

(*test files except those that start with 2 or 3).

Also not that unless the nobareglobqual option is enabled or you use |s within them, trailing (...) glob grouping operators conflict with glob qualifiers. For example, in !(foo) or ^(foo), the foo would be treated as a glob qualifier. You'd need ^foo or !(foo)(#q) (the (#q) adds a non-bare (explicit) glob qualifier).

cuonglm
  • 153,898
  • I am using zshell, ^ works for normal files, but it doesn't work for dot files. imagine I have a bunch of dot files. .a, .b, .c, .d ... . I want to remove all of them but keep .a and .b, using rm ^(.a|.b) doesn't work. Is there a way to do that? – Aaron Shen Dec 01 '15 at 08:53
  • 1
    You need to use D qualifier to include dot file in globbing result, try print -rl -- ^(.a|.d)(D) – cuonglm Dec 01 '15 at 09:05
6

When you use setopt extendedglob you can use ^(2test|3test) to remove all files except 2test and 3test:

# setopt extendedglob
# touch {1..10}test
# rm ^(2test|3test)
# ls
2test 3test

! is used by bash, zsh however uses ^.

Marco
  • 33,548
0

starting with a clean (empty) zsh

 zsh -f 
 setopt extendedglob
 touch {1..10}test
 rm ^(2|3)test(.)
zzapper
  • 1,140