6

Does anyone know if there is a way to incorporate repetition constraints into ZSH wildcard expressions?

For example, to match all files starting with "ABC" following by one or more numbers, using grep one could do:

ls | grep -e "ABC[0-9]\+"

Is there any way to do this directly with ZSH glob strings? Something along the lines of:

ls "ABC[0-9]\+"

I've looked through the docs for ZSH and Googled for something like this, but so far have not found any such support.

Anyone know if this is possible?

  • 1
    if you scroll down to 14.8.1 Glob Operators you'll find <[x]-[y]> and x##... so in your case <-> or [0-9]## (with setopt extendedglob) – don_crissti Nov 23 '16 at 14:04
  • Great! I completely missed that -- was looking for something like \+ or {n,}. This works perfectly. – Keith Hughitt Nov 23 '16 at 14:07
  • You could do it with + too if you used kshglob (it's on the same page): print -rl ABC+([0-9]) though keep in mind you also need unsetopt bareglobqual if not in ksh emulation mode. – don_crissti Nov 23 '16 at 14:14
6

Yes, use ## to match one or more occurrence of [0-9] like:

ABC[0-9]##

This requires extendedglob to be set, which is by default. If unset, set it first:

setopt extendedglob

Example:

% print -l ABC*
ABC
ABC75475
ABC8
ABC90

% print -l ABC[0-9]##
ABC75475
ABC8
ABC90
| improve this answer | |
  • Great! Works perfectly. Out of curiosity, do you know if there is any similar syntax for specifying a min/max number of repeats? e.g. something like "[0-9a-z]{2,5}"? – Keith Hughitt Nov 23 '16 at 14:08
  • @KeithHughitt Just added this :) – heemayl Nov 23 '16 at 14:09
  • I saw that you added note about extendedglob immediately after I posted comment, so I edited that part out. Thanks for including that! – Keith Hughitt Nov 23 '16 at 14:12
  • @KeithHughitt regarding your second requirement, i don't think this can be done with globbing with zsh (at least i'm not aware of one). Btw you can take a look at numeric range matching via <X-Y> – heemayl Nov 23 '16 at 14:14
  • @heemayl: you can, using (#cN,M) globbing flag. – cuonglm Nov 23 '16 at 14:58
3

With extendedglob enabled:

$ setopt extendedglob
$ print -rl -- perl[[:digit:]]##
perl5

or with kshglob enabled and bareglobqual disabled:

$ setopt kshglob
$ unsetopt bareglobqual
$ print -rl -- perl+([[:digit:]])
perl5

Note that using [:digit:] to match everything considered digit in current locale. If you want to match 0 through 9 only, set LC_ALL=C or using literally [0123456789].


You can specify number of match, like regular expression {n,m} using (#cN,M) globbing flag anywhere that # and ##operators can be used, except in (*/)# and (*/)##:

$ print -rl -- perl[[:digit:]](#c1)
perl5
$ print -rl -- perl[[:digit:]](#c2)
zsh: no matches found: perl[[:digit:]](#c2)
| improve this answer | |
2

Scroll down to “Filename Generation”, and enable either extended_glob (which should be the default, but isn't for backward compatibility) or ksh_glob. Both zsh's extended globs and ksh's have the full power of regular expressions.

ERE syntax      ksh glob      zsh extended glob
(foo)*          *(foo)        (foo)#
(foo)+          +(foo)        (foo)##
(foo)?          ?(foo)        (|foo)
(foo|bar)       @(foo|bar)    (foo|bar)

Keep in mind that most tools that use regular expressions use them as search patterns that must match a substring, but globs are always used as patterns that must match the whole string. For example foofoobar does not match the zsh glob (foo)##, since after foofoo there's some text left over.

Zsh has additional operators that don't extend the expressive power but make some expressions easier to write. The operators ^ and ~ (extended_glob) and !(…) (ksh_glob) provide negation, e.g. ^foo or !(foo) matches anything except foo, which in regex syntax requires the unwieldy |[^f].*|f[^o].*|fo[^o].*. The operator <…-…> (zsh-specific, does not require extended_glob) matches any integer (in decimal notation) in a range, e.g. <3-11> matches 3 and 10 but not 30 or 1.

So, excluding issues like unprintable characters, ls | grep -e "ABC[0-9]\+" can be written in ways such as

print -lr -- *ABC[0-9]##*(N)      # requires extended_glob
print -lr -- *ABC+([0-9])*(N)      # requires ksh_glob
print -lr -- *ABC+<->(N)

But since “one or more digit then anything” is equivalent to “a digit then anything”, it can also be written

print -lr -- *ABC[0-9]*(N)
| improve this answer | |

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.