← ALL TILS
TIL

zsh does not word-split unquoted variables like bash does

bash splits an unquoted $flags string into separate arguments. zsh does not: SH_WORD_SPLIT is off by design, so the command gets one argument and rejects it. Use an array, or force the split with the = expansion flag.

A deploy wrapper worked under bash and fell over under zsh. It collected flags in a string and expanded them unquoted:

opts="--silent --retry 3"
curl $opts https://example.com/health

bash splits $opts into three arguments. zsh hands curl ONE argument, the whole string, and curl exits with an unknown-option error. zsh does not word-split unquoted parameter expansions; SH_WORD_SPLIT is off by design.

Three fixes, best first:

  • Use an array, which is what the string was pretending to be: opts=(--silent --retry 3); curl $opts ...
  • Force the split at one call site: curl ${=opts} ...
  • setopt SH_WORD_SPLIT, which changes every expansion in the whole script. Blunt.

The array is the honest fix. A list stored as a string is a bug waiting for a shell change.

zshbashshellportability