On Fri, 13 Oct 2023 13:02:30 -0400 Ti Strga <wearyofallthisc...@gmail.com> wrote:
> First off, I have a feeling that GMail is going to garble the line > wrapping in this message; I cannot get it to stop being "helpful". > Apologies if that happens. > > I've encountered some behavior that I cannot find described anywhere in > the man page, and I'm hoping to learn whether it's a bug (it seems like > unintended behavior) or just a quirk for hysterical raisins. If it's > the latter then I'm also hoping there's a BASH_COMPAT level that might > adjust the behavior, although I'll state right now that I have no idea > whether previous versions behaved any differently. > > The summary is that if a parameter is set specifically for a '.'/'source' > command, and the source'd file calls 'exec' to run another script, then > that exec'd script cannot unset the parameter; if we want that parameter > to not be present in the exec'd script, then the source'd file must do > the unsetting prior to exec. > > We're running this... > > $ declare -p BASH_VERSINFO > declare -ar BASH_VERSINFO=([0]="5" [1]="2" [2]="15" [3]="3" > [4]="release" [5]="x86_64-pc-cygwin") > > ...although the platform [5] doesn't seem to matter; the same behavior > was reported to me on Linux as well as what I'm observing on Cygwin. I > did not have a chance to verify the Linux behavior firsthand. > > > === Background (or, I Promise This Isn't Just Code Golf) > > The example reproduction here is a calling script "outer" sourcing > "inner.sh". The real world situation is that "inner.sh" is a small > library of shell functions and environment variable setup for our workflow, > and multiple top-level scripts each '.' that library. > > The games here with exec are to support scripts that might be running for > a long time. For those we want the script to make a temporary copy of > itself and exec the temp copy, so that potential updates to the installed > scripts don't hose up the long-running shell when it suddenly reads from > a different point in the script.[*] The way it's implemented, the author > of the top-level script can simply set a parameter when sourcing the > library; the library makes the copy and performs the exec. When the copy > sets the same parameter and sources the library, the library detects the > cloning and will not keep doing it. (The library also fixes up what gets > reported as "name of current script" for error messages and whatnot, but > none of that is shown here as it doesn't change the weird behavior.) > > [*] Alternatively, there's the trick about putting the entire script > contents inside a compound statement to force the parser to read it all, > but that just makes the script harder for a human to read. Copy-and-exec > makes the top-level scripts cleaner IMHO. > > The kicker is that the parameters that trigger all this behavior must be > unset before going on with the remainder of the library and back to the > calling script. If not, then anytime a "cloned" script might call any > other script, that will be cloned as well even if its author did not write > anything saying to do that. (And in a couple cases, the scripts actually > start an interactive subshell; if the parameters get exported to there, > then "CLONE ALL THE THINGS" behavior just keeps propagating through the > scripts. Hilarity ensues.) > > > === Reproduction Bash employs dynamic scoping and the behaviour of unset can be confusing in some cases (with local variables in particular). However, given the code that you presented, I am unable to reproduce the claimed behaviour in any of 5.1.16, 5.2.15 and the devel branch. In the absence of a minimal reproducible example, it is difficult to comment. Incidentally, echo is not ideal as a tool for determining the state of a given variable. Consider "declare -p INSIDE OUTSIDE" instead. -- Kerin Millar