As it can be seen, ${…​:#…​} substitution is a filtering of array, which by default filters-out elements ( (M) flag induces the opposite behavior). When used with string, not an array, it behaves similarily: returns empty string when {input_string_var:#pattern} matches whole input string.

Side-note: (M) flag can be used also with ${(M)var#pattern} and other substitutions, to retain what’s matched by the pattern instead of removing that.

Multi-line matching like with grep

Suppose you have a Subversion repository and want to check if it contains files being not under version control. You could do this in Bash style like follows:

local svn_status="$(svn status)" if [[ -n "$(echo "$svn_status" | \grep \^\?)" ]]; then echo found fi

That are 3 forks: for svn status , for echo and for grep . This can be solved by :# substitution and (M) flag described above in this section (just check if the number of matched lines is greater than 0). However, there’s a more direct approach:

local svn_status="$(svn status)" nl=$'

' if [[ "$svn_status" = *((#s)|$nl)\?* ]]; then echo found fi

This requires extendedglob . The (#s) means: "start of the string". So ((#s)|$nl) means "start of the string OR preceded by a new-line".

If the extendedglob option cannot be used for some reason, this can be achieved also without it, but essentially it means that alternative (i.e. | ) of two versions of the pattern will have to be matched:

setopt localoptions noextendedglob local svn_status="$(svn status)" nl=$'

' if [[ "$svn_status" = (\?*|*$nl\?*) ]]; then echo found fi

In general, multi-line matching falls into the following idiom ( extendedglob version):

local needle="?" required_preceding='[[:space:]]#' [[ "$(svn status)" = *((#s)|$nl)${~required_preceding}${needle}* ]] && echo found