How to make the (N) patterns of the zsh eatable by bash?

I am trying to develop a script which runs (and, ideally, does the same 🙂 ) in zsh and Bash. Problem is, that at a point, the zsh-specific part contains a pattern ending with (N). So: this_pattern*(N). In zsh, that makes the pattern expand to nothing if it has no match.

Bash says for that, "syntax error by unexpected symbol". Note, execution on Bash never happens on this zsh-specific part, and this error message is caused by the parsing code!

Could I somehow help Bash to eat it, for example to not parse it?

Asked By: peterh

||

From zsh_expn:

  N      sets the NULL_GLOB option for the current pattern

So, the bash way would be to set the nullglob option for that pattern.

( shopt -s nullglob || setopt NULL_GLOB && yourcommand this_pattern* )

would work, and set that option just for the subshell spawned by (…). (Either shopt or setopt fails, being a bash and zsh built-in, respectively, but the one that does work sets the option to make patterns without matches disappear without error. && and || have left-to-right evaluation.)

Note that of course this lacks beauty (sorry, @StéphaneChazelas!), and subshell creation comes at a performance penalty. But you want portability, so you can’t really use a zsh-specific feature.

Answered By: Marcus Müller

If it’s in a zsh-specific part, you can avoid bash complaining upon parsing that code that it will never run by doing:

eval 'cmd this_pattern*(N)'

instead of

cmd this_pattern*(N)

That eval command is valid in bash from a syntax PoV. It would only become invalid if eval was run in which case it would try to interpret the cmd this_pattern*(N) code that is invalid in bash.

Now, in this specific case,

cmd this_pattern*(N)

Is actually valid in bash (though means something completely different) if you enable the extglob option, upon which bash recognises a subset of ksh’s extended glob operator including *(something) meaning 0 or more somethings.

So doing:

if [ -n "$BASH_VERSION" ]; then
  shopt -s extglob
fi

beforehand would avoid the parsing error in bash in this case. It wouldn’t help for a *.txt(N) pattern for instance though.

Answered By: Stéphane Chazelas