ksh: := doesn't work as expected

$ typeset -i a b=42
$ echo ${a:=b}
0    # expecting `42', or at least `b'
$ echo $a
     # definitely expecting `42', zsh prints `0' here
$ 

This works fine on mksh, and partly on bash.

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 18

Most upvoted comments

@McDutchie Can’t find a flaw, thanks for the fix

@McDutchie - you are right. That means that whenever typeset -i a; a="something" “something” should be evaluated as an arithmetic expression, for example like this (this works):

radziecki> typeset -i a; a="2+2"
radziecki> echo $a
4

Now I begin to think that problem is somewhere else:

radziecki> set -o nounset
radziecki> typeset -i a
radziecki> echo "${a}"
ksh93: a: parameter not set
radziecki> echo "${a?please set a}"
0
radziecki> echo "${a:?please give a value}"
0
radziecki> echo "${b:?please give b value}"
ksh93: b: please give b value
radziecki> echo "${b?please set b}"        
ksh93: b: please set b
radziecki> echo "${b+xx}"

radziecki> echo "${a+xx}"

Looks like sometimes (? modifier, :? modifier, := modifier) a is assumed to have a zero integer value, otherwise a is unset. We simply do not reach the assignment phase because because ksh concluded a already has a value (well, sometimes)

Actually, POSIX leaves room for such extensions.

This is going off topic, but I was referring only to what I quoted, not the := part. I.e. even the most basic form of an assignment like a=b can take b literally (posix) or as $b (ksh when a is an integer - which might have been set elsewhere at the code). That’s the part I was referring to.

The := behavior is an obvious extension of the basic rule quoted earlier that value assigned to an integer var is evaluated as an arithmetic expression.

FWIW, this behavior, e.g. typeset -i a; b=5; a=b; echo $a works the same and prints 5 also in bash and mksh (both even in posix mode) and in other pdksh derivatives like pdksh itself and openbsd sh.

@avih Actually, POSIX leaves room for such extensions.

If parameter is unset or null, quote removal shall be performed on the expansion of word and the result (or an empty string if word is omitted) shall be assigned to parameter. In all cases, the final value of parameter shall be substituted.

Agreed, we should not expect the variable b to have its value of 42 expanded as the b in echo ${a:=b} refers to the string of “b” as @saper pointed out. If you wanted 42 then echo ${a:=$b} should be used.

I do not agree, because in the reproducer, that variable has been declared an integer with typeset -i. And that means assignment values for that variable are parsed as arithmetic expressions, even without $(()) being used. In arithmetic expressions, variable names that are not expanded by the shell (i.e. those without a leading $) are replaced by their numeric values by the arithmetic subsystem. So, for an a of an integer type, a simple assignment a=b will assign the numeric value of b to a. Since ${a:=b} and ${a=b} are just another form of assignment (though they are performed conditionally and yield a value), they should work the same way.

Compare with mksh, which does this right:

$ arch/*/bin/ksh -c 'echo $KSH_VERSION; typeset -i a b=42; a=b; echo $a'
Version AJM 93u+m/1.0.0-alpha+dev 2021-02-15
42
$ arch/*/bin/ksh -c 'echo $KSH_VERSION; typeset -i a b=42; echo ${a:=b}'
Version AJM 93u+m/1.0.0-alpha+dev 2021-02-15
0
$ mksh -c 'echo $KSH_VERSION; typeset -i a b=42; a=b; echo $a'
@(#)MIRBSD KSH R59 2020/05/05
42
$ mksh -c 'echo $KSH_VERSION; typeset -i a b=42; echo ${a:=b}'          
@(#)MIRBSD KSH R59 2020/05/05
42

Confirmed on all versions of ksh93 down to s+ 1993-12-28 through ksh2020. Anther one that is going to be challenging to track down… Thanks for the report.