Nothing inspires a short blog writing like the “Eureka!” moment you get when something which was evading you clicks into place.
Occasionally I come across a situation where I have a shell command which needs to execute in both Bash and Z-Shell. This is when you realise that despite the similarity of the two shells, they’re subtly different beasts.
Consider this really simple snippet of code, which does three things:
- Sets a list of semicolon separated values in a string.
- Sets the
IFSvariable as a semicolon.
- Iterates over the items of the semicolon separated values and prints each one on its own line.
bash-3.2$ test='alpha;bravo;charlie;delta' bash-3.2$ IFS=';' bash-3.2$ for item in $test; do echo ">>> $item"; done >>> alpha >>> bravo >>> charlie >>> delta
But in Z-Shell it doesn’t work:
$ echo $SHELL /bin/zsh $ test='alpha;bravo;charlie;delta' $ IFS=';' $ for item in $test; do echo ">>> $item"; done >>> alpha;bravo;charlie;delta
So, what’s going on? Why does Z-Shell ignore the
IFS variable? Well, the answer is that it behaves this way by default for reasons I’m not entirely clear on. My guess is it’s because Z-Shell supports native arrays, which Bash doesn’t. Therefore the assumption was probably made that you’d use Z-Shell’s far more robust array support than the old-skool approach of splitting strings.
But, there’s a workaround (isn’t there always?). You need to enable string splitting manually with
setopt, which the following one-liner will achieve regardless of which shell you run it in:
setopt SH_WORD_SPLIT 2>&1; IFS=';'