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 "$...@]}" } client() { local a=1 b=1 blackbox_by_ref a b; declare -p a b # Ok: a=bar, b...@] a='ls /;true'; blackbox_by_ref "$a" b # No oops } client # Ok: a=bar, b...@] 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