Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: Zsh's and ksh's bang (!) expansion flags have different meanings



>>> In ksh, the (!) flag can be used to list the keys of arrays and associative arrays. In that regard, it's closer to Zsh's (k) flag than Zsh's (!) flag

The (k) flag was originally intended to mimic ksh's ! name prefix (due
to ! colliding with history).  The character "k" was chosen as a
mnemonic for "like (k)sh".

Funny, I always assumed it was (k)ey (and (v)alue). Good thing that both ksh and key start with the same letter :-)

I suppose, rather than introduce (!), I
could have extended (k) for the named reference behavior, to preserve
that similarity.  Could still try that if it seems a good idea.

No, I don't think this would be wise. Zsh's (!) and ksh's ! really do two different things.

Zsh's (!) means don't dereference the parameter and use its value as is. For references, it implies using the referent name. For other types of parameters it has no effect. Afaik, there is no equivalent mechanism in ksh, which ALWAYS fully dereferences the parameter.

Ksh's ! means return the name of the referred variable instead of its value, or, if there are multiple elements, print the names/keys/indexes of the referred elements instead of their values. The latter part matches what Ksh's (k) performs for associative tables.

Even though Zsh's (!) and ksh's ! perform two fundamentally different things, there is one case where they produce the same result. Consider the following definitions:

typeset -a var0=(abc def ghi)
typeset -n ref1=var0
typeset -n ref2=ref1

In Zsh, the (!) in ${(!)ref1} means don't dereference ref1 and use its value as is, so the substitution returns the string "var0". In ksh, the ! in ${!ref1} means return the name of the variable referred by ref1, which is the name of the variable var0, so, like in Zsh, the string "var0". This equality of results is however limited to direct variable references with no subscripts. In Zsh, ${(!)var0} and ${(!)ref2} respectively return "abc def ghi" and "ref1", while in ksh ${!var0} and ${!ref2} both return "var0" and in Zsh ${(!)ref1[1]} returns "v" while in ksh ${!ref1[1]} returns "var0[1]".

This shows that even though Zsh's (!) and ksh's ! use the same character flag and sometimes happen to return the same value, they really should not be considered as being related; they perform two fundamentally different things.

I guess this would conflict with nameref-to-assoc, which currently does what I would expect. (Unless I'm missing something).
% typeset -n n=a
% echo ${n}
1 2
% echo ${(k)n}
a b
% typeset -A a=( a 1 b 2 )

Indeed. For reference, ksh;
$ typeset -A foo=([a]=1 [b]=2)
$ typeset -n bar=foo
$ echo ${!bar}
foo
$ echo "${!bar[@]}"
a b

Given Zsh's default unsubscripted-array expansion behavior, it seems like it would be hard to maintain the distinction between "what does this nameref point to" and "what are that thing's keys" if those semantics were combined in a single flag.

Fully agree.

With the introduction of named references, Zsh's emulation of ksh's ! has been changed to map to Zsh's (!k). I guess that was done for the case of direct variable references where Zsh's (!) happens to perform the same as ksh's ! but that combination breaks the example above and makes it difficult to explains what Zsh's emulation of ksh's ! performs.

% typeset -A foo=([a]=1 [b]=2)
% typeset -n bar=foo
% emulate ksh
% echo "${!bar}"
foo
echo "${!bar[@]}"'
foo

I think that this should be reverted such that Zsh's emulation of ksh's ! maps to Zsh's (k) as it used to do. It's true that the first result will then no longer match ksh's but the second will. I think it's a better trade-off because it doesn't try to do two different things at the same time and thus is easier to explain and understand.

I will prepare a patch…

Philippe



Messages sorted by: Reverse Date, Date, Thread, Author