bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: variable set in exec'ing shell cannot be unset by child shell


From: Robert Elz
Subject: Re: variable set in exec'ing shell cannot be unset by child shell
Date: Sat, 14 Oct 2023 03:33:32 +0700

    Date:        Fri, 13 Oct 2023 14:21:59 -0400
    From:        "Dale R. Worley" <worley@alum.mit.edu>
    Message-ID:  <87bkd2idrs.fsf@hobgoblin.ariadne.com>

  | but I coded an instance of
  | your description (above), and it does not show the dubious behavior that
  | you report.

Your test isn't accomplishing what is desired I believe.

I can't speak directly to what is happening with bash, as I don't look
at its source code (absurd GNU licensing nonsense prevents it) but I
believe we have a similar problem in the NetBSD shell (but without the
wrinkle added by bash's "novel" interpretation of what unset should mean)
for which I have been designing a fix over the past week or two (not
inspired in any way by this issue in bash, I've been working on it, and
pondering it earlier, longer than that).

The issue we have (which possibly might be similar in bash, but only
possibly - but it would explain the symptoms) is that when one does

        VAR=value command

"VAR" is essentially made a local variable for command, so its value
in the outlying environment is unchanged by the assignment of value.

When command is an external command (something from the filesystem)
this all works just fine, and there are no issues, command is run,
the local variable is removed, and the previous global remains intact.

But when the command is a function, or a shell builtin (or the '.'
command, which is almost both of those) then we have some strange
effects.

Consider the following code in command (which can be a function, or
a file executed via '.' (or in bash, source))

        echo $VAR
        VAR=foo
        echo $VAR

now that should (and is almost certain to) print "value" and then "foo".

When command is finished, VAR will be back to being whatever it was
before command was executed, because of the nature of it being considered
local.

But that's wrong, all "VAR=foo command" is supposed to do, is to put
VAR into the environment of command, without altering it in the shell
environment that is executing command.   If command is a function, or
a '.' script (or a shell builtin, which was the context in which I
first considered this issue) which alters VAR, the global VAR should
be altered - that it was initialised in command from its environment
should be irrelevant (this is why external commands have no issues, they
have no way to even attempt to alter the values of variables in the calling
shell).

Now in bash, this get weirder, because of its strange interpretation of
what "unset" should do to a local variable (assuming that this is what
is happening in bash as well, and this is just a guess) when the variable
wasn't declared local inside the function which does the unset (but somewhere
else - which is the case here, in fact, there is no function at all).

That would be why adding the "unset OUTSIDE" "fixes" things, you're using
a very quirky bash trait to work around a bigger problem - that unset removes
the local variable, and gives you access to the global again, which is what
you wanted.   (If that "unset" worked properly, and simply marked the local
variable as being unset, then it wouldn't help to fix things at all).

I suspect that a simpler fix might be to change

        OUTSIDE=clone . inner

into

        OUTSIDE=clone
        . inner

The former is semi undefined (at best) as to its effect according to
POSIX, and certainly overkill for what you're doing, since you are
actually wanting to operate on a global variable, and inner is going
to change its value, worrying about what it is, or rather would be,
in the outer script, after the ". inner" command, if inner did not
alter OUTSIDE is kind of unnecessary.

In this variant, there is nothing even smelling like a local variable,
just the global OUTSIDE, which will get updated however that happens,
when inner finishes, whatever it left in OUTSIDE will be what the outer
script sees, no attempt to restore what the outer script used to have
is required.

I'll leave it to Chet, or perhaps one of the others here who do understand
bash internals to confirm whether this is a likely explanation for the
issue, or at least an approximation of ot.

If it is, it is non-trivial, but I believe possible, to fix.
But note I'm not finished with fixing my version of this problem yet,
so I really cannot yet be certain about this.

kre




reply via email to

[Prev in Thread] Current Thread [Next in Thread]