I would discourage the use of "l" (ell) as a variable name for readability.
I like the fact that _blackbox_called_by uses a parameter rather than parsing the caller's name. This allows you to permit multiple callers to a common private function, if needed. However, I'm assuming that you will name that function and _blackbox_safe_vars more generically since I'm also assuming that there will be many public/private function pairs that will be able to make use of them. On Sun, May 2, 2010 at 6:35 AM, Freddy Vulto <fvu...@gmail.com> wrote: > Here's another revised version. > > I figured I could use bash internal `printf -v' just before the `eval' > to check if the variable name is a valid identifier. This should be an > exceptional case, so I don't mind doing this at the end of the function > - checking just before the eval. Now it's also easy to see the eval is > safe (provided you don't make typos on the right hand side, see > http://mywiki.wooledge.org/BashFAQ/006, thanks). > > I moved conflict checking to a central reusable function > "_blackbox_safe_vars()" because one needs to do this for *every* library > function which is returning variables by reference, in my opinion, unless > you're willing to obfuscate all your local variables and still wait for a > conflict to happen? > > The code below works around the following bash caveats?: > - local variables conflict with variables to be returned by reference > - `local' only lists variables having a value > - `printf -v' can't be used to assign to an array > > Here's the code: > > # Check whether variable name(s) conflicts with local(s) > # Params: $1 Local var names (output of 'local') > # $* Variables to check > # Return: False (1) if error > _blackbox_safe_vars() { > local IFS=$'\n' l v > for l in $1; do > for v in "${@:2}"; do > [[ ${l%=*} == $v ]] && echo "ERROR: ${FUNCNAME[1]}:"\ > "\`$v': conflicts with local" 1>&2 && return 1 > done > done > return 0 > } > > # Check whether private function is being called by public interface > # Params: $1 Public function > # Return: False (1) if error > _blackbox_called_by() { > [[ ${FUNCNAME[2]} != $1 ]] && { echo "ERROR: ${FUNCNAME[1]}:"\ > "MUST be called by $1()" 1>&2; return 1; } || return 0 > } > > # Private library function. Do not call directly. See blackbox() > _blackbox() { > _blackbox_called_by blackbox || return 1 > local a b c d e f g h i j k=( foo "bar cee" ) > # ... > # Lots of library code here, not more than one screen though ;-) > # ... > printf -v"$2" %s b # Return value > printf -v"$3" x && eval $3=\(\"\$...@]}\"\) # Return array > return 0 # Return status > } > > # Param: $1 input argument > # Param: $2 variable name to return value to > # Param: $3 variable name to return array value to > # Public library function > blackbox() { > # NOTE: Give all locals a value so they're listed with 'local' > local __2= __3= __x= > _blackbox_safe_vars "$(local)" "$2" "$3" || return 1 > _blackbox "$1" __2 __3 # Call private > __x=$? # Catch status > printf -v"$2" %s "$__2" # Return value > printf -v"$3" x && eval $3=\(\"\${_...@]}\"\) # Return array > return $__x # Return status > } > > blackbox i a b; printf $'%s\n' $a "$...@]}" # Outputs vars ok > blackbox i __2 __3 # Outputs error > d='ls /;true'; blackbox i "$d" "$d" # No oops > _blackbox a # Force public access > > > Freddy Vulto > http://fvue.nl/wiki/Bash:_passing_variables_by_reference > > >