8

This works in bash:

touch a b c
echo !(a)

If I execute above script in zsh (with kshglob on), it complains:

zsh: number expected

If I add | after a, it works:

echo !(a|)

Why?

12

Because in this case, it conflicts with bare glob qualifiers since it's at the end of the pattern. *(a1) is taken as the files last accessed in the last day. (a1) is treated as a glob qualifier. So in your !(a) case, zsh complains about the missing number of days after the a glob qualifier (here applied to the file called !).

In zsh globs, (...) grouping is mostly used for the (foo|bar) alternation, so adding a | is a documented way to make sure a trailing (...) is not treated as a glob qualifier.

Another documented alternative is to double the parenthesis (!((a))) or you could add an empty glob qualifier (like !(a)(-)).

To completely remove that ambiguity, one can turn off the bare_glob_qual option (set +o bareglobqual), after which glob qualifiers have to be written with the extendedglob (#q...) syntax (*(#qa1) here).

The kshglob option (added in 1998, roughly the same time bash added its extglob though bash didn't have any extended glob before that) is mostly there for the ksh emulation mode (emulate ksh), for zsh to be able to run ksh scripts, where kshglob is enabled and bareglobqual is disabled. When it was first introduced, after enabling kshglob, you'd need to specify glob qualifiers as -(...) to avoid that kind of conflict but that caused too much confusion and conflicted with the @-(...) syntax of ksh93, the (#q...) and bareglobqual options were introduced later.

zsh users generally prefer zsh's own extended glob (set -o extendedglob) operators which are easier to type (for most) and more powerful (than the ksh88 ones enabled with kshglob also found in bash -O extglob).

For example, !(foo) would be written ^foo. The !(foo|)bar equivalent would however be longer like (^(foo|))bar.

Other ksh88 -> zsh translations:

  • *(x) -> x#
  • +(x) -> x##
  • @(x|y) -> x|y
  • ?(x) -> (x|)

Some ksh93 -> zsh translations:

  • ~(i:x) -> (#i)x (case insensitive)
  • ~(N)x -> x(N) (nullglob, originated in zsh)
  • {1,5}(x) -> x(#c1,5)
  • @(foo&bar) -> foo~^bar or ^(^foo|^bar)

Some only found in zsh:

  • <1-23> (range of decimal numbers)
  • pattern~except
  • pattern(glob-qualifier) (the killer feature of zsh globs)
  • (pattern/)# (any level of subdirectories matching the pattern; the **/ simplified version of (*/)# was also added to ksh93 and bash recently)
  • ***/* (recursive globbing following symlinks).
  • (#a1)foobar (approximate matching, allowing some errors, here 1)
  • ...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.