Use a helper function upvar
:
# Assign variable one scope above the caller.
# Usage: local "$1" && upvar $1 value [value ...]
# Param: $1 Variable name to assign value to
# Param: $* Value(s) to assign. If multiple values, an array is
# assigned, otherwise a single value is assigned.
# NOTE: For assigning multiple variables, use 'upvars'. Do NOT
# use multiple 'upvar' calls, since one 'upvar' call might
# reassign a variable to be used by another 'upvar' call.
# See: http://fvue.nl/wiki/Bash:_Passing_variables_by_reference
upvar() {
if unset -v "$1"; then # Unset & validate varname
if (( $# == 2 )); then
eval $1=\"\$2\" # Return single value
else
eval $1=\(\"\${@:2}\"\) # Return array
fi
fi
}
And use it like this from within Newfun()
:
local "$1" && upvar $1 new
For returning multiple variables, use another helper function upvars
. This allows passing multiple variables within one call, thus avoiding possible conflicts if one upvar
call changes a variable used in another subsequent upvar
call.
See: http://www.fvue.nl/wiki/Bash:_Passing_variables_by_reference for helper function upvars
and more information.
The problem with:
eval $1=new
is that it's not safe if $1
happens to contain a command:
set -- 'ls /;true'
eval $1=new # Oops
It would be better to use printf -v
:
printf -v "$1" %s new
But printf -v
cannot assign arrays.
Moreover, both eval
and printf
won't work if the variable happens to be declared local
:
g() { local b; eval $1=bar; } # WRONG
g b # Conflicts with `local b'
echo $b # b is empty unexpected
The conflict stays there even if local b
is unset
:
g() { local b; unset b; eval $1=bar; } # WRONG
g b # Still conflicts with `local b'
echo $b # b is empty unexpected