I'm on zsh 5.8.1
We know that zsh does word splitting before parameter expansion. Therefore, i.e.
x="a b c"
foo $x
passes a single parameter to foo
. Now consider this example:
set -u # Error if uninitialized variable
a() { echo "/$1/" }
x= # Variable initialized, but empty
a $x
I would expect that a
is called with a single parameter holding the empty string, and I get as output //
. This is indeed true when I call either
a ''
or
a "$x"
but if I do just a
a $x
the set -u
fires and I get an error a: 1: parameter not set.
I have the feeling that I somehow misinterpret the way zsh expands the command line. Could someone explain to me, why $1
is taken as uninitialized? In bash, I would understand it, but why in zsh?
Some of the confusion here may stem from the fact that zsh
"fixed" the handling of scalar variables, but for reasons that may be lost to history, a similar update did not happen with arrays. Since command line parameters are essentially a special case of an array, the same issues occur when processing those.
The "fix" that's being referred to here is that zsh
will not perform word-splitting on unquoted scalar variables by default, whereas many other shells (e.g. bash
) will. That means that scalars don't need to be quoted just to handle white space:
#usr/bin/env bash
args() { echo "/$1/" "/$2/"; }
s="aaa bbb"
args $s
#=> /aaa/ /bbb/
args "$s"
#=> /aaa bbb/ //
#usr/bin/env zsh
args() { echo "/$1/" "/$2/" }
s="aaa bbb"
args $s
#=> /aaa bbb/ //
args "$s"
#=> /aaa bbb/ //
But when creating an array in zsh
and other shells, unquoted empty variables are simply elided. The variables are re-parsed in a way that kind of resembles word-splitting, and it's unexpected when zsh
does that.
#usr/bin/env zsh
s=
a=(one $s three)
typeset -p a
#=> typeset -a a=( one three )
Parameters are converted to an array as part of the argument processing (they are even available in an array named argv
), so the parsing behavior is the same:
#usr/bin/env zsh
args() { echo "/$1/" "/$2/" }
s=
args one $s three
#=> /one/ /three/
As others have noted, this behavior is a misfeature. It reduces one of zsh
's advantages - quoting is still required in a number of situations to ensure values are preserved.
It may be that the scalar change was primarily included to simplify handling of filenames with spaces (after all, shells are often used to process files), and nobody thought it was necessary to extend that philosophy to arrays and parameter lists. Darn.