Is it possible to pass a variable to a zsh function, and modify its value without using eval? I came across a bash way to do that: local -n: https://stackoverflow.com/a/50281697
1 Answer
typeset -n is actually originally from ksh93 (1993). bash added something similar in 4.3 released in 2014. In ksh93, it's much more useful as ksh93 does static scoping (where you can't access local variables from the caller unless they're exported) instead of dynamic in bash/zsh (where a function always sees the local variables of its caller¹). See also:
$ bash -c 'function f { typeset -n var=$1; var=x;}; var=0; f var; echo "$var"' environment: line 0: typeset: warning: var: circular name reference environment: warning: var: circular name reference environment: warning: var: circular name reference x $ ksh -c 'function f { typeset -n var=$1; var=x;}; var=0; f var; echo "$var"' x zsh has no nameref support though it has variable dereference operators which can be used both for expansion and assignment.
$ expand() print -r -- ${(P)1} $ assign() : ${(P)1::=$2} $ var=foo $ expand var foo $ assign var bar $ print -r -- $var bar Or you can always use the standard approach using eval which should work in any Bourne-like shell:
expand() { eval 'printf "%s\n" "${'"$1"'}"' } assign() { eval "$1=\$2" } and before you ask, no, that doesn't make it more or less unsafe than using the P flag or namerefs in bash provided it's done properly like here. Regardless of the approach, it's important the name of the variable is properly controlled.
assign var "$external_input" is safe, assign "$external_input" value or expand "$external_input" is not whether you use P, namerefs or eval. Same as with [ -v "$external_input" ] or read "$external_input" which are all arbitrary command execution vulnerabilities in bash/ksh/zsh.
See also that discussion on the zsh-workers mailing list on the merits of the various approaches at doing indirect assignment.
And that discussion from 2001 where adding ksh93-style nameref support to zsh was being considered. And again in 2015 and in 2023, showing that there's interest in adding that feature, and it will likely end up being added in the next version after 5.9 whether that's 5.10 or 6.0.
¹ Though see the zsh/param/private module in zsh for variables to become private to a function.
- Explanations: *
(P)and the like are parameter expansions * The firstexpandandassignfunction definitions omit the curly brackets, which are optional for single-line definitions * The:character (ussed inassign) is the "no-op" command; e.g. explained herejuanmirocks– juanmirocks2022-12-02 14:44:24 +00:00Commented Dec 2, 2022 at 14:44 - note that this doesn't work for array variables; if you attempt to assign an array through a nameref using (P), you just get a string with space-separated items.Mark Reed– Mark Reed2023-06-15 19:07:41 +00:00Commented Jun 15, 2023 at 19:07