Is there a POSIX Compliant way to limit the scope of a variable to the function it is declared in? i.e.:
Testing()
{
TEST=\"testing\"
}
Testing
echo \"T
It's possible to simulate local variables in a Posix Shell using a small set of general functions.
The sample code below demonstrates two functions, called Local and EndLocal, which do the trick.
All functions are short, and use descriptive names, so they should be relatively easy to understand.
They support variables with tricky characters like spaces, single and double quotes.
Caution: They use global variables beginning with LOCAL_, so there's a small risk of collision with existing homonym variables.
The Test routine recursively calls itself 3 times, and modifies a few local and global variables.
The output shows that the A and B local variables are preserved, contrary to the global N variable.
Code:
#!/bin/sh
#-----------------------------------------------------------------------------#
# Manage pseudo-local variables in a Posix Shell
# Check if a variable exists.
VarExists() { # $1=Variable name
eval "test \"\${${1}+true}\" = \"true\""
}
# Get the value of a variable.
VarValue() { # $1=Variable name
eval "echo \"\${$1}\""
}
# Escape a string within single quotes, for reparsing by eval
SingleQuote() { # $1=Value
echo "$1" | sed -e "s/'/'\"'\"'/g" -e "s/.*/'&'/"
}
# Set the value of a variable.
SetVar() { # $1=Variable name; $2=New value
eval "$1=$(SingleQuote "$2")"
}
# Emulate local variables
LOCAL_SCOPE=0
Local() { # $*=Local variables names
LOCAL_SCOPE=$(expr $LOCAL_SCOPE + 1)
SetVar "LOCAL_${LOCAL_SCOPE}_VARS" "$*"
for LOCAL_VAR in $* ; do
if VarExists $LOCAL_VAR ; then
SetVar "LOCAL_${LOCAL_SCOPE}_RESTORE_$LOCAL_VAR" "SetVar $LOCAL_VAR $(SingleQuote "$(VarValue $LOCAL_VAR)")"
else
SetVar "LOCAL_${LOCAL_SCOPE}_RESTORE_$LOCAL_VAR" "unset $LOCAL_VAR"
fi
done
}
# Restore the initial variables
EndLocal() {
LOCAL_RETCODE=$?
for LOCAL_VAR in $(VarValue "LOCAL_${LOCAL_SCOPE}_VARS") ; do
eval $(VarValue "LOCAL_${LOCAL_SCOPE}_RESTORE_$LOCAL_VAR")
unset "LOCAL_${LOCAL_SCOPE}_RESTORE_$LOCAL_VAR"
done
unset "LOCAL_${LOCAL_SCOPE}_VARS"
LOCAL_SCOPE=$(expr $LOCAL_SCOPE - 1)
return $LOCAL_RETCODE
}
#-----------------------------------------------------------------------------#
# Test routine
N=3
Test() {
Local A B
A=Before
B=$N
echo "#1 N=$N A='$A' B=$B"
if [ $N -gt 0 ] ; then
N=$(expr $N - 1)
Test
fi
echo "#2 N=$N A='$A' B=$B"
A="After "
echo "#3 N=$N A='$A' B=$B"
EndLocal
}
A="Initial value"
Test
echo "#0 N=$N A='$A' B=$B"
Output:
larvoire@JFLZB:/tmp$ ./LocalVars.sh
#1 N=3 A='Before' B=3
#1 N=2 A='Before' B=2
#1 N=1 A='Before' B=1
#1 N=0 A='Before' B=0
#2 N=0 A='Before' B=0
#3 N=0 A='After ' B=0
#2 N=0 A='Before' B=1
#3 N=0 A='After ' B=1
#2 N=0 A='Before' B=2
#3 N=0 A='After ' B=2
#2 N=0 A='Before' B=3
#3 N=0 A='After ' B=3
#0 N=0 A='Initial value' B=
larvoire@JFLZB:/tmp$
Using the same technique, I think it should be possible to dynamically detect if the local keyword is supported, and if it's not, define a new function called local that emulates it.
This way, the performance would be much better in the normal case of a modern shell having built-in locals.
And things would still work on an old Posix shell without it.
Actually we'd need three dynamically generated functions: