Search code examples
bashkshtypeset

Why does typeset has different results on bash and ksh when used inside/out of a function?


Code:

#test.sh
test_func(){
var1="$1"
var2="$1"
echo "var1 before typeset: $var1"
echo "var2 before typeset: $var2"
typeset -l var1="$1"
typeset -l var2
echo "var1 after typeset: $var1"
echo "var2 after typeset: $var2"
}

test_func "$1"

var1="$1"
var2="$1"
echo "var1 before typeset: $var1"
echo "var2 before typeset: $var2"
typeset -l var1=$1
typeset -l var2
echo "var1 after typeset: $var1"
echo "var2 after typeset: $var2"

output using bash with input SAMPLE_INPUT:

var1 before typeset: SAMPLE_INPUT
var2 before typeset: SAMPLE_INPUT
var1 after typeset: sample_input
var2 after typeset: 
var1 before typeset: SAMPLE_INPUT
var2 before typeset: SAMPLE_INPUT
var1 after typeset: sample_input
var2 after typeset: SAMPLE_INPUT

output using ksh:

var1 before typeset: SAMPLE_INPUT
var2 before typeset: SAMPLE_INPUT
var1 after typeset: sample_input
var2 after typeset: sample_input
var1 before typeset: sample_input
var2 before typeset: sample_input
var1 after typeset: sample_input
var2 after typeset: sample_input

I dont understand why bash clobbers var2 in a function and why ksh changing the variable inside a function affects the value of it outside the function when re grabbing the value from $1. I assume this has something to do with their implementation of local vs global variables.

my desired output would be the same on bash and ksh and inside/outside of a function:

var before typeset: SAMPLE_INPUT
var after typeset: sample_input

used out of function:

var before typeset: SAMPLE_INPUT
var after typeset: sample_input

Solution

  • In bash, typeset is an alias for declare, which makes variables local when invoked inside a function unless the argument -g is added.

    In ksh, typeset has different behavior depending on how you defined your function: With test_func() { ... } variables created with typeset are global; with function test_func { ... } they're local. Because you're using the test_func() { syntax, in your ksh code, typeset refers to the preexisting global variable instead of creating a local; so the original value is retained.

    If you want identical behavior, you need to move typeset outside of the function, or add -g when the shell is bash; the latter can be done with a parameter expansion that emits -g only when BASH_VERSION is set, as below:

    test_func(){
      var="$1"
      echo "var before typeset: $var"
      typeset ${BASH_VERSION+-g} -l var="$1"
      echo "var after typeset: $var"
    }
    
    var="SAMPLE_INPUT"
    test_func "$var"
    echo "var after function: $var"
    

    ...properly emits:

    var after function: sample_input
    

    ...even on bash.


    As for the empty value, you get that even on ksh if you change your function declaration syntax to make typeset create locals by default:

    function test_func {
      typeset -l var
      var="$1"
    }
    
    test_func "SAMPLE_INPUT"
    echo "Result is: <$var>"
    

    ...emits, Result is: <> when run in ksh.