ksh "typeset -i" and Empty Parameters

I'm getting different behaviors for "typeset -i" on different systems. In one case unset parameters are 0, and in another case they're empty. Is one of these behaviors correct, or is the behavior here unspecified?

First system:

$ typeset -i x
$ print $x
0
$ print ${.sh.version}
Version M 93t+ 2009-05-01
$ uname
AIX
$ oslevel -s
7100-01-05-1228

Second system:

$ typeset -i x
$ print $x

$ print ${.sh.version}
Version M-12/28/93e
$ uname
AIX
$ oslevel -s
6100-04-11-1140

There are many ksh variations, mostly 88 and 93. Typesetting controls many useful characteristics, and I like the zero for nothing. The general idea is to allow ksh to store it in integer not ascii, which speeds things up and ensures you have no fractional parts. However, my old hpux ksh8, ksh93, bash all return nil not zero.

$ bash -c 'typeset -i x ; echo ">$x<"'
><
$ dtksh -c 'typeset -i x ; echo ">$x<"'
><
$ ksh -c 'typeset -i x ; echo ">$x<"'  
><
$ strings `whence ksh` | grep -i version        Wed May 8 15:06:57 EDT 2013
@(#)Version 11/16/88
version
$ strings `whence dtksh` | grep -i version
@(#)DesktopVersionString
%d: invalid binary script version
Unrecognized version
Version not defined
@(#)Version 12/28/93
@(#)Version M-12/28/93
is a shell builtin version of
versions
versions
is a shell builtin version of
@(#)Version M-12/28/93d
.sh.version
%d: invalid binary script version
@(#)dtksh:        $XConsortium: version.c /main/5 1996/08/30 15:36:49 drk $
version
C_VERSION
OS_VERSION
VERSION
VERSION
VERSION
XCU_VERSION
$ bash --version
GNU bash, version 4.2.39(1)-release (hppa2.0w-hp-hpux11.11)
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
sdmw504a-ttyp7-/dhome1/n/nbkodln
$

David Korn is still alive last I checked, so you can ask him. http://en.wikipedia.org/wiki/David\_Korn\_\(computer_scientist\) He answered me on my issue of inability to concatente <(...) into longer single string arguments (it is always a word, and breaks free of adjacent strings).

That was my intention in this case. I wasn't actually looking to format the variable. I'm just using it as a binary flag. If the flag is not empty then my code was assuming that it had been deliberately set. This worked fine until I deployed on AIX 7.1, as shown in the original post.

At this point I've made the code portable by removing "-i" from the typeset command. I'm still curious, though, if either way is correct, or of the language leaves this unspecified.

typeset -i x=0

should give some portability.?

Yeah, but I'm looking to portably implement the other behavior, where an unset parameter is empty.

Yes, one should avoid reading what one did not write, or read very critically! :smiley:

One shell / O/S I wandered onto had ksh set to nounset => reject reading nonexistent variables. None of my scripts worked! I had to turn that off quick in my .profile ! Talk about well know defaults, isn't "${some_name_never_set}" well known to be "" ?

It makes code more maintainable to not rely on defaults unless they are well known. For a boolean, you might use set and unset, and skip the content entirely.

---------- Post updated at 03:51 PM ---------- Previous update was at 03:50 PM ----------

AIX writes their own ksh?

Even if I unset the parameter the "Version M 93t+ 2009-05-01" ksh insists on telling me the value is 0:

$ typeset -i x
$ print $x
0
$ unset x
$ print $x
0
$ set |grep x=
x=0

According to the standards, if an equivalent of the command:

set -u

has been run, and an equivalent of the command

set +u

has not been run to counteract it, then:

Otherwise, ${unknown_variable} expands to an empty string.

Maybe typeset is beyond set/unset, like a tattoo? :smiley:

No need to typeset, just unset or set x and test x for being set.

Somewhere in the man page as copies way below, ksh promises you can test just set and unset, no typeset, but I cannot see it, so maybe just go for blank or not. Your test cost is mostly variable lookup:

$ dtksh -xc '
 set x ; echo ColonEqualSet  ${x:=Right};echo x=$x
 unset x ; echo ColonEqualUnset  ${x:=Right};echo x=$x
 set x ; echo ColonMinusSet  ${x:-Right};echo x=$x
 unset x ; echo ColonMinusUnset  ${x:-Right};echo x=$x
 set x ; echo ColonPlusSet  ${x:+Right};echo x=$x
 unset x ; echo ColonPlusUnset  ${x:+Right};echo x=$x
 set x ; echo EqualSet  ${x=Right};echo x=$x
 unset x ; echo EqualUnset  ${x=Right};echo x=$x
 set x ; echo MinusSet  ${x-Right};echo x=$x
 unset x ; echo MinusUnset  ${x-Right};echo x=$x
 set x ; echo PlusSet  ${x+Right};echo x=$x
 unset x ; echo PlusUnset  ${x+Right};echo x=$x
 set x ; echo ColonQuerySet  ${x:?Right};echo x=$x
 unset x ; echo ColonQueryUnset  ${x:?Right};echo x=$x
 set x ; echo QuerySet  ${x?Right};echo x=$x
 unset x ; echo QueryUnset  ${x?Right};echo x=$x
'
+ set x
+ echo ColonEqualSet Right
ColonEqualSet Right
+ echo x=Right
x=Right
+ unset x
+ echo ColonEqualUnset Right
ColonEqualUnset Right
+ echo x=Right
x=Right
+ set x
+ echo ColonMinusSet Right
ColonMinusSet Right
+ echo x=Right
x=Right
+ unset x
+ echo ColonMinusUnset Right
ColonMinusUnset Right
+ echo x=
x=
+ set x
+ echo ColonPlusSet
ColonPlusSet
+ echo x=
x=
+ unset x
+ echo ColonPlusUnset
ColonPlusUnset
+ echo x=
x=
+ set x
+ echo EqualSet Right
EqualSet Right
+ echo x=Right
x=Right
+ unset x
+ echo EqualUnset Right
EqualUnset Right
+ echo x=Right
x=Right
+ set x
+ echo MinusSet Right
MinusSet Right
+ echo x=Right
x=Right
+ unset x
+ echo MinusUnset Right
MinusUnset Right
+ echo x=
x=
+ set x
+ echo PlusSet
PlusSet
+ echo x=
x=
+ unset x
+ echo PlusUnset
PlusUnset
+ echo x=
x=
+ set x
dtksh: line 15: x: Right

${parameter:-word}

If parameter is set and is non-null then substitute its value. Oth-
erwise substitute word.

word is not evaluated unless it is to be used as the substituted
string.

In the following example, pwd is executed only if d is not set or
is NULL:

print ${d:-$(pwd)}

If the colon ( : ) is omitted from the expression, the shell only
checks whether parameter is set or not.

${parameter:=word}

If parameter is not set or is null, set it to word. The value of
the parameter is then substituted. Positional parameters cannot be
assigned to in this way.

word is not evaluated unless it is to be used as the substituted
string.

In the following example, pwd is executed only if d is not set or
is NULL:

print ${d:-$(pwd)}

If the colon ( : ) is omitted from the expression, the shell only
checks whether parameter is set or not.

${parameter:?word}

If parameter is set and is non-null, substitute its value. Other-
wise, print word and exit from the shell , if the shell is not
interactive. If word is omitted then a standard message is printed.

word is not evaluated unless it is to be used as the substituted
string.

In the following example, pwd is executed only if d is not set or
is NULL:

print ${d:-$(pwd)}

If the colon ( : ) is omitted from the expression, the shell only
checks whether parameter is set or not.

${parameter:+word}

If parameter is set and is non-null, substitute word. Otherwise
substitute nothing.

word is not evaluated unless it is to be used as the substituted
string.

In the following example, pwd is executed only if d is not set or
is NULL:

print ${d:-$(pwd)}

If the colon ( : ) is omitted from the expression, the shell only
checks whether parameter is set or not.

Well, I'm in a function here, and I don't want to name clash, so I use typeset. The function is a widely-used utility function. I suppose I could follow some naming convention to help prevent clashes, but using typeset to create a local parameter seems cleaner. Preventing the name clash is my main reason for using typeset. Adding "-i" was kind of an afterthought to help performance, but the unexpected result on Version M 93t+ 2009-05-01 was that "-i" causes empty to show up as 0.

Name clashes will be. Does typeset do anything just assigning a value to the variable does not? All variables are local unless exported, and cannot escape their subshell in in any case. I assume shells keep unexported variables in a higher priority local container, to search before going to the exported flat ascii environment container.

So, forget about -i and just use:

typeset x=""

From this discussion, it should be clear that declaring a variable to have an integer value AND expecting it to be able to hold an empty string does not produce portable behavior.

Right, and variables explicitly declared with "typeset" can't escape their function. For example:

x="outside f"
function f
{
    typeset x="inside f"
}
f
print $x
outside f

---------- Post updated at 06:49 PM ---------- Previous update was at 06:46 PM ----------

Yeah, that's what I ended up doing.

Yeah, I'll just consider the behavior of empty integers to be unspecified.

If a function gets put on a pipe, all bets are off, anyway. If outside X is getting stomped by inside x, there is (), but I guess typeset is cheaper. It forces a local variable generation without interrogation of the two existing variable stores for variables to use.

Is it really maintainable long term even for you to have inside x and outside x floating around the same script?

For boolean, the -i is worth it only if you get c boolean with it like in 'x &&', '(( x || y ))' and such. I guess have 1 and 0 is familiar. I rarely use a boolean, maybe using flow instead.