Local and readonly variables in shells
I've no idea how shells implement them. In this article, I will pretend they use a stack based implementation.
Ksh93 never pops out values. When dash encounteres 'local', it just reuse the outer value. Bash and pdksh/mksh unset values first.
Considering the following script: (no-readonly.sh)
x='global'
g() {
local x
x='g'
readonly x
echo 'x in g is' $x
}
f() {
local x
x='f'
readonly x
echo 'x in f is' $x
g
echo 'after run g, x in f' is $x
}
echo global x is $x
f
echo global x is still $x
dash, bash, and mksh work. But ksh fails. After running g, x is still 'g'.
Image the stacks:
scope| assignments | ksh dash bash/pdksh/mksh
-----+----------------+----------------------------------------------
top | x='global' | |global |global |global
f | local x | |global |global ||global
f | x='f' | |f|global |f|global |f||global
g | local x | |f|global |f|global ||f||globas
g | x='g' | |g|f|global |g|f|global |g||f||global
f | | |g|f|global |f|global |f||global
top | | |g|f|global |global |global
Considering readonly, things goes tricky. ksh/dash seems just refuse to push values to readonly variables. bash refuses to push values to global variables, but not local variables. pdksh/mksh pushes normally.
dash's behaviour is not expected but reasonable.
From the very moment you say local var
, the var
inherits values
and attributes from the outer scope automatically.
So you cannot assign new value to it.
Though it's werid to inheriting values and attributes from the outer
scope.
Bash is not consistent on top level and function level, which is intentional to provide special protection for global readonly variables.
Conclusion
pdksh/mksh is my best friend.
Usually Bash won't bite me if I be careful about global variable names.
For dash, I guess sed -e 's/readonly//' script.mksh > script.dash
will work.
Test scripts
I've wrote some tiny test scripts, and put them in this repo.
Update
zsh works as expected, too.