[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Passing variables by reference conflicts with local
From: |
Freddy Vulto |
Subject: |
Re: Passing variables by reference conflicts with local |
Date: |
Wed, 5 May 2010 23:34:12 +0200 |
User-agent: |
Mutt/1.5.18 (2008-05-17) |
Thanks, but I think I found a very nice, less complicated solution, and
as I now understand from the investigation of the intricacies of `unset'
elsewhere in this thread, it is perfect legitimate. A speed comparison
between all different solutions/workarounds would be interesting though?
My solution is to explicitly define the return variables "local" in the
callee. Then the callee can unset them again from a called helper
function, effectively making the variable appear down the call-stack to
the caller.
To summarize: I'm seeing this technique of 'passing by reference'
popping up on the web:
blackbox() { eval $1=bar; }
f() { local b; blackbox b; declare -p b; }
f # Looks ok: b=bar
but having the caveats of an unsafe `eval' and this `local' conflict:
blackbox() { local b; eval $1=bar; }
f() { local b; blackbox b; declare -p b; }
f # Error: b=, because blackbox() has a `local b' declared
The solution is to have a helper function `_return' like this:
_return() { unset -v "$1" && eval $1=\"\$2\"; }
blackbox() { local b && _return b bar; }
f() { local b; blackbox b; declare -p b; }
f # Ok: b=bar
Or a more elaborate example, which is also capable of returning arrays
by reference:
a=A; unset -v b # Initialize globals for test case
# Return variables by reference
# Param: $1 Variable name to return value to
# Param: $* Value(s) to return. If multiple values, an array is
# returned, otherwise a single value is returned.
_return_by_ref() {
if unset -v "$1"; then # Unset & validate varname
if [ $# -eq 2 ]; then
eval $1=\"\$2\" # Return single value
else
eval $1=\(\"\${@:2}\"\) # Return array
fi
fi
}
# Example public library function
# Param: $1 Variable to return value to
# Param: $2 Variable to return array to
blackbox_by_ref() {
local a b c d e f=(foo "bar \"cee" $'cee\n dee') # etc.
# ...
# non-obfuscated variables used here :-)
# ...
# return values
local "$1" && _return_by_ref $1 "bar"
local "$2" && _return_by_ref $2 "${f[@]}"
}
client() {
local a=1 b=1
blackbox_by_ref a b; declare -p a b # Ok: a=bar, b=f[@]
a='ls /;true'; blackbox_by_ref "$a" b # No oops
}
client # Ok: a=bar, b=f[@] and error because of oops test
declare -p a b # Ok: Globals are unchanged (a=A, b=nil)
It seems to work all right on bash-2.05b, bash-3.0, bash-3.2, bash-4.0,
bash-4.1.
The `eval' could be replace by `printf -v', but this requires bash >
3.1-alpha1 and the eval is relative safe because the varname is checked
by `unset'?
So now we can stop condemning people who're passing variables by
reference and write even more beautiful bash code! ...
...until we find another caveat ;-) am I overlooking something?
Freddy Vulto
http://fvue.nl/wiki/Bash:_passing_variables_by_reference
- Re: Passing variables by reference conflicts with local, (continued)
Re: Passing variables by reference conflicts with local, Marc Herbert, 2010/05/04