views:

32

answers:

2

I have encountered an issue with ksh programs running differently on ksh88 & ksh93 wherein functions which call functions handle differently, variables declared with typeset and export. Here is an example program that highlights the difference:

#!/bin/ksh
# example.ksh: highlights differences between typeset and export on ksh93
function inner
{
  echo "  Inside inner, before assignment, TEST_VALUE=[$TEST_VALUE]"
  TEST_VALUE=abc
  echo "  Inside inner, after assignment, TEST_VALUE=[$TEST_VALUE]"
}
function outer_typeset
{
  typeset TEST_VALUE
  echo "Inside outer_typeset, before call of inner, TEST_VALUE=[$TEST_VALUE]"
  inner
  echo "Inside outer_typeset, after call of inner, TEST_VALUE=[$TEST_VALUE]"
}
function outer_typeset_x
{
  typeset -x TEST_VALUE
  echo "Inside outer_typeset_x, before call of inner, TEST_VALUE=[$TEST_VALUE]"
  inner
  echo "Inside outer_typeset_x, after call of inner, TEST_VALUE=[$TEST_VALUE]"
}
function outer_export
{
  export TEST_VALUE
  echo "Inside outer_export, before call of inner, TEST_VALUE=[$TEST_VALUE]"
  inner
  echo "Inside outer_export, after call of inner, TEST_VALUE=[$TEST_VALUE]"
}

outer_typeset
unset TEST_VALUE
echo
outer_typeset_x
unset TEST_VALUE
echo
outer_export

The result when run on a Linux box running ksh93 follows:

$ echo ${.sh.version}
Version M 1993-12-28 r

$ ./example.ksh
Inside outer_typeset, before call of inner, TEST_VALUE=[]
  Inside inner, before assignment, TEST_VALUE=[]
  Inside inner, after assignment, TEST_VALUE=[abc]
Inside outer_typeset, after call of inner, TEST_VALUE=[]

Inside outer_typeset_x, before call of inner, TEST_VALUE=[]
  Inside inner, before assignment, TEST_VALUE=[]
  Inside inner, after assignment, TEST_VALUE=[abc]
Inside outer_typeset_x, after call of inner, TEST_VALUE=[]

Inside outer_export, before call of inner, TEST_VALUE=[]
  Inside inner, before assignment, TEST_VALUE=[]
  Inside inner, after assignment, TEST_VALUE=[abc]
Inside outer_export, after call of inner, TEST_VALUE=[abc]

As you can see when TEST_VALUE is typeset, the value of TEST_VALUE set in inner is lost when control returns to the outer function. When TEST_VALUE is declared via export, the value set in inner is retained when control returns to outer.

Since there is not a new processes invoked when the outer function calls the inner function, I do not see why export should be used in order for the variable to keep scope in the sub function. Also I have noted that typeset -x behaves the same as typeset whereas I would have expected typeset -x to be equivalent to export.

When I run this program on a machine running ksh88 (AIX, Solaris, HP-UX) or pdksh (Linux) or MKS KornShell, typeset, typeset -x, and export behave the same in this example.

For now in I have changed typeset to export in order to provide compatibility on ksh93 for the programs using similar code that were developed and tested on ksh88.

Perhaps this is a ksh93 defect?

A: 

The ksh93 manual has this to say about typeset:

When invoked inside a function defined with the function name syntax, a new instance of the variable vname is created, and the variable's value and type are restored when the function completes.

And this about export:

the given names are marked for automatic export to the environment of subsequently-executed commands.

In particular, nothing is restored when you use export inside a function.

It seems likely that typeset is intended to provide the functionality provided by "procedure-local variables" in other languages. I wouldn't call that a design defect.

Norman Ramsey
I must agree. Exporting a variable declared typeset sounds like invoking undefined behavior.
Joshua
I agree and the ksh man page on my system confirms that typeset creates a new local variable. The crux seems to be that functions invoked inside a function declared with function keyword, after typeset'ing the variable, do not have access to the local variable, just as the outer program that called the first function does not have access to it.
Bernard Assaf
After all this--I found a ksh93 FAQ: http://www.kornshell.com/doc/faq.html. Q28, section III says: Q28. How are variables scoped in ksh?A28. The scoping of variables was not defined for ksh88 but in ksh93 static scoping was specified. For example the output from function f1 { print foo=$foo } function f2 { typeset foo=local f1 } foo=global f2will be "global". To get f2 to cause f1 to print the local value of foo, f2 can run "foo=$foo f1" instead.* * *This was exactly the behavior I observed. The typeset variable is isolated from global and sub-function scope.
Bernard Assaf
A: 

Your script has calls to inner and inner_function but the latter is not defined. Is this just a typo in the question or does your actual script have this error also?

The behavior you show in your output is correct.

Try changing the definition of outer_typeset_x from function outer_typeset_x { to outer_typeset_x () { and you'll see that the output will be the same for it as for outer_export.

Dennis Williamson
Thanks Dennis for pointing this out. Originally I had inner_function and outer_function but to shorten the code for this forum I changed them to drop "_function" in this post. The actual code was changed and you are correct, the typo is in the question; the actual script did not.
Bernard Assaf
As for the proposed fix, if I change from function to (), then all three outer functions indeed yield the same result as stated: inside each outer function, TEST_VALUE is abc after call to inner function.
Bernard Assaf
It might be useful, for some, to know that Bash does not make this distinction between using `function` or not when defining a function. `declare` (or `typeset`) always makes a variable local. Also, Bash has different rules about variables in functions. The output of your script with `#!/bin/bash` instead of ksh has all three groups of output looking like the last group.
Dennis Williamson

related questions