functions can fully unset local vars in other scopes
Configuration Information [Automatically generated, do not change]: Machine: x86_64 OS: linux-gnu Compiler: gcc Compilation CFLAGS: -march=x86-64 -mtune=generic -O2 -pipe -fno-plt -DDEFAULT_PATH_VALUE='/usr/local/sbin:/usr/local/bin:/usr/bin' -DSTANDARD_UTILS_PATH='/usr/bin' -DSYS_BASHRC='/etc/bash.bashrc' -DSYS_BASH_LOGOUT='/etc/bash.bash_logout' -DNON_INTERACTIVE_LOGIN_SHELLS uname output: Linux t420 5.15.55-2-lts #1 SMP Fri, 22 Jul 2022 23:29:06 + x86_64 GNU/Linux Machine Type: x86_64-pc-linux-gnu Bash Version: 5.1 Patch Level: 16 Release Status: release Description: `bash' does not let `unset' fully undeclare local variables. (so that they can be used later as `local' variables without needing to redeclare them I assume.) bash-5.1$ x=abc; declare -p x; unset -v x; declare -p x declare -- x="abc" bash: declare: x: not found bash-5.1$ a () { local x=abc; local -p x; unset -v x; local -p x ;} bash-5.1$ a declare -- x="abc" declare -- x However, other functions are allowed to delete those variables: bash-5.1$ a () { local x=abc; b; local -p x ;} bash-5.1$ b () { unset -v x ;} bash-5.1$ a bash: local: x: not found This enables defininng a "really_unset" function like so: really_unset () { unset "$@" ;} Which may be useful I guess. But I think allowing functions to unset local variables from other functions defeats the whole purpose of having that `unset' behaviour. This enables `local' variable to unexpectedly become global after a function is called. Fix: I think calling `unset -v x' (where `x' is a local variable not in the current scope) should behave as if it was called in the scope of `x', so `x' should remain declared in that scope with no attributes and no value. It may be nice to also add a "force" option for `unset' that makes it actually unset the variable if it is `local'. Since this could be useful in some cases and it won't be possible after the behaviour is changed.
Re: functions can fully unset local vars in other scopes
> It may be nice to also add a "force" option for `unset' that makes it > actually unset the variable if it is `local'. Since this could be > useful in some cases and it won't be possible after the behaviour is > changed. Clarification I meant that it should fully undeclare local variables in the current scope, not that it should be able to fully undeclare variables from other scopes. Now that I think about it, it does not really make sense to implement it like a "force" option. I think adding a "-l" option that behaves similarly to "-v", but only works on local varnames of the current scope and fully undeclares them would make more sense.
Re: functions can fully unset local vars in other scopes
On Fri, Jul 29, 2022 at 03:59:07AM +0200, Emanuele Torre wrote: > This enables defininng a "really_unset" function like so: > > really_unset () { unset "$@" ;} > > Which may be useful I guess. https://lists.gnu.org/archive/html/bug-bash/2010-05/msg00045.html https://fvue.nl/wiki/Bash:_passing_variables_by_reference
Re: functions can fully unset local vars in other scopes
2022年7月29日(金) 10:59 Emanuele Torre : > Description: > `bash' does not let `unset' fully undeclare local variables. (so that > they can be used later as `local' variables without needing to > redeclare them I assume.) > > [...] > > However, other functions are allowed to delete those variables: This is a documented behavior: >From Bash Reference Manul - 3.3 Shell Functions https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Shell-Functions > The unset builtin also acts using the same dynamic scope: if a > variable is local to the current scope, unset will unset it; > otherwise the unset will refer to the variable found in any calling > scope as described above. If a variable at the current local scope > is unset, it will remain so until it is reset in that scope or until > the function returns. Once the function returns, any instance of the > variable at a previous scope will become visible. If the unset acts > on a variable at a previous scope, any instance of a variable with > that name that had been shadowed will become visible. There has been a long discussion before. https://lists.gnu.org/archive/html/bug-bash/2018-02/threads.html#00065 https://lists.gnu.org/archive/html/bug-bash/2018-03/threads.html#0 Also, I would like to repeat myself in that thread. The dynamic unsetting has existing applications [see the following my reply]: https://lists.gnu.org/archive/html/bug-bash/2018-03/msg00020.html > This enables defininng a "really_unset" function like so: > > really_unset () { unset "$@" ;} > > Which may be useful I guess. This is a well-known idiom and is already used in some places as `unlocal'. https://github.com/scop/bash-completion/blob/36ceb272ddf7ef70b7fa79c5c3686080b1510054/bash_completion#L248-L263 https://github.com/akinomyoga/ble.sh/blob/0b95d5d900b79a63e7f0834da5aa7276b8332a44/src/util.sh#L388-L404 This is also used for Freddy Vulto's upvars trick as Greg has cited. > But I think allowing functions to unset local variables from other > functions defeats the whole purpose of having that `unset' > behaviour. This enables `local' variable to unexpectedly become > global after a function is called. I think these two cases will never be mixed. Because when a user does not intend to remove the variable placeholder of the previous scopes, the user unsets a variable that is declared within the same function. When a user intends to remove that of the previous scopes, the `local-variable name' can become global, but it is just what the user expects. > Fix: > I think calling `unset -v x' (where `x' is a local variable not in the > current scope) should behave as if it was called in the scope of `x', > so `x' should remain declared in that scope with no attributes and no > value. This is what `shopt -s localvar_unset' does. > It may be nice to also add a "force" option for `unset' that makes it > actually unset the variable if it is `local'. Since this could be > useful in some cases and it won't be possible after the behaviour is > changed. For this purpose, you can always use `unlocal' referenced above. -- Koichi
Re: functions can fully unset local vars in other scopes
Thank you, Koichi and Greg. I didn't notice that this was intended and documented. Also it is good to know that `localvar_unset' exists. Cheers.