Использовать вспомогательную функцию 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
}
И используйте это так изнутри Newfun()
:
local "$1" && upvar $1 new
Для возврата нескольких переменных используйте другую вспомогательную функцию upvars
. Это позволяет передавать несколько переменных в одном вызове, что позволяет избежать возможных конфликтов, если один вызов upvar
изменяет переменную, используемую в другом последующем вызове upvar
.
См. http://www.fvue.nl/wiki/Bash:_Passing_variables_by_reference для вспомогательной функции upvars
и более подробную информацию.
Проблема с:
eval $1=new
в том, что небезопасно, если $1
содержит команду:
set -- 'ls /;true'
eval $1=new # Oops
Было бы лучше использовать printf -v
:
printf -v "$1" %s new
Но printf -v
не может назначать массивы.
Более того, eval
и printf
не будут работать, если переменная объявлена local
:
g() { local b; eval $1=bar; } # WRONG
g b # Conflicts with `local b'
echo $b # b is empty unexpected
Конфликт сохраняется, даже если local b
равен unset
:
g() { local b; unset b; eval $1=bar; } # WRONG
g b # Still conflicts with `local b'
echo $b # b is empty unexpected