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

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



In some recent work I noticed that Zsh's and ksh's bang (!) expansion flags have different meanings. I don't think this is problematic but other people might disagree and as this difference wasn't obvious to me I thought it was worth mentioning here.

In Zsh, the (!) flag means "Don't dereference references", while in ksh it means "Print the parameter's name instead of its value". The difference is the most striking on chains of references:

Zsh

Zsh in ksh emulation

ksh


typeset var0=foo

typeset -n ref1=var0 ref2=ref1 ref3=ref2

echo var0:${(!)var0}

echo ref1:${(!)ref1}

echo ref2:${(!)ref2}

echo ref3:${(!)ref3}

emulate ksh

typeset var0=foo

typeset -n ref1=var0 ref2=ref1 ref3=ref2

echo var0:${!var0}

echo ref1:${!ref1}

echo ref2:${!ref2}

echo ref3:${!ref3}


typeset var0=foo

typeset -n ref1=var0 ref2=ref1 ref3=ref2

echo var0:${!var0}

echo ref1:${!ref1}

echo ref2:${!ref2}

echo ref3:${!ref3}

var0:foo

ref1:var0

ref2:ref1

ref3:ref2

var0:foo

ref1:var0

ref2:ref1

ref3:ref2

var0:var0

ref1:var0

ref2:var0

ref3:var0


In Zsh, the (!) flag only makes a difference when the parameter is a reference, while in ksh it always makes a difference; it always replaces the parameter's value with the parameter's name. 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:

Zsh

Zsh in ksh emulation

ksh


typeset str=abc

typeset -a arr=(aa bb cc)

typeset -A ass=(X xx Y yy Z zz)

echo "str   :${(k)str}"

echo "str[1]:${(k)str[1]}"

echo "str[*]:${(k)str[*]}"

echo "arr   :${(k)arr}"

echo "arr[1]:${(k)arr[1]}"

echo "arr[*]:${(k)arr[*]}"

echo "ass   :${(k)ass}"

echo "ass[Y]:${(k)ass[Y]}"

echo "ass[*]:${(k)ass[*]}"

emulate ksh

typeset str=abc

typeset -a arr=(aa bb cc)

typeset -A ass=([X]=xx [Y]=yy [Z]=zz)

echo "str   :${!str}"

echo "str[1]:${!str[1]}"

echo "str[*]:${!str[*]}"

echo "arr   :${!arr}"

echo "arr[1]:${!arr[1]}"

echo "arr[*]:${!arr[*]}"

echo "ass   :${!ass}"

echo "ass[Y]:${!ass[Y]}"

echo "ass[*]:${!ass[*]}"


typeset str=abc

typeset -a arr=(aa bb cc)

typeset -A ass=([X]=xx [Y]=yy [Z]=zz)

echo "str   :${!str}"

echo "str[1]:${!str[1]}"

echo "str[*]:${!str[*]}"

echo "arr   :${!arr}"

echo "arr[1]:${!arr[1]}"

echo "arr[*]:${!arr[*]}"

echo "ass   :${!ass}"

echo "ass[Y]:${!ass[Y]}"

echo "ass[*]:${!ass[*]}"

str   :abc

str[1]:a

str[*]:abc

arr   :aa bb cc

arr[1]:1

arr[*]:aa bb cc

ass   :X Y Z

ass[Y]:Y

ass[*]:X Y Z

str   :abc

str[1]:b

str[*]:abc

arr   :aa

arr[1]:1

arr[*]:aa bb cc

ass   :

ass[Y]:Y

ass[*]:X Y Z

str   :str

str[1]:str[1]

str[*]:0

arr   :arr

arr[1]:arr[1]

arr[*]:0 1 2

ass   :ass

ass[Y]:ass[Y]

ass[*]:X Y Z


One difference is that in Zsh "${(k)ass[Y]}" returns just "Y", while in ksh "${!ass[Y]}" returns "ass[Y]". To me, the former looks more consistent with the behavior for "ass[*]".

Another difference is that in Zsh the (k) flag doesn't work for array ranges ("${(k)arr[*]}" returns "aa bb cc"), while in ksh it does ("${!arr[*]}" returns "0 1 2"). To me, this looks like something that ought to be changed in Zsh. In conjunction with the (v) flag, it could be used to list array values interleaved with their indexes.

Philippe



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