Re: Issue with VAR=foo cmd where VAR is a named reference
Philippe Altherr <[email protected]> Wed, 15 Jul 2026 20:46:08 +0200
| Newsgroups | gmane.comp.shells.zsh.devel |
|---|---|
| Message-ID | <CAGdYchtxypuuUt8ssN+1uemiLSDzb_R39S4wdTJ2KJJYSTDA2g@mail.gmail.com> |
--0000000000004b939f0656aabd53
Content-Type: text/plain; charset="UTF-8"
>
> In my opinion, an assignment before a command should be the same as a
>> normal assignment as far as possible with the addition of adding the
>> export of the explicitly named variable.
>>
>
> In principle, I agree with these requirements however I think that there
> is an even more imperative requirement, namely that "cmd" must run in
> exactly the same environment when run with "ref=foo cmd" as when run with
> "fun() { cmd }; ref=foo fun". This requirement puts severe constraints on
> what's possible. If you consider only the case "ref=foo cmd", then you
> could modify the implementation of inline assignments for example to
> augment the environment with the pair "ref"->"foo" just after the call to
> addvars and before the execution of "cmd". However, nothing similar is
> possible for the case "fun() { cmd }; ref=foo fun". Here, your only
> levers are to modify the parameter table that will be visible to "fun" in
> such a way that when "fun" executes "cmd", the environment will be the
> desired one.
>
> For our discussion we should consider the following example:
>
> var=foo
> typeset -n ref=var
> fun() {
> typeset -p var ref
> printenv var
> printenv ref
> }
> ref=bar fun
>
> What we need to answer is what should the "typeset -p" and the two
> "printenv" print. What the implementation of "ref=bar fun" can do is
> change the value of ref and/or var and modify their flags. We can also
> decide whether -x is allowed on references and what it has for effect. We
> could possibly also define one or more new parameter flags (although there
> are only very few PM bits left).
>
Technically, it would be possible to go beyond by (ab)using the precomputed
environment but I don't think this would be wise.
My understanding of the Zsh language is that whenever an external command
is run (with no inline assignments), it is expected to run in an
environment that is fully specified by the set of -x flagged parameters
present in the scope in which the command is run; an entry N->V is present
in that environment if and only if the scope of the command contains an -x
flagged parameter named N and the string representation of its value is the
string V.
This can be implemented by computing, each time an external command is run,
a new environment that contains the names and values of all the -X flagged
parameters visible at that moment. The current implementation of Zsh
optimizes this by maintaining a precomputed environment. Each time a
parameter is flagged with -x, a corresponding entry is added to the precomputed
environment and each time the value of an -x flagged parameter is modified,
the corresponding entry is updated. Whenever an external command is run,
the precomputed environment already contains all the expected entries and
can be used as the environment for the command.
Oliver stated that he expects the following:
export var=foo; typeset -n ref=var; ref=bar env - export ref=bar var=bar
This implies that the inline assignment "ref=bar" should A) assign "bar" to
the parameter referred by "ref", here "var" and B) add the entry
"ref"->"bar" to the environment of the inline command. This could be
achieved by executing "ref=bar" as a normal assignment and then adding the
entry "ref"->"bar" to the precomputed environment. The consequence would be
that the "precomputed" environment would no longer simply be a
precomputation of the environment implied by the -x flagged parameters. It
would instead become an entity of its own.
Consider the following example:
% fun() { typeset -p var ref; echo var=$(printenv var); echo ref=$(printenv
ref) }
% export var=foo; typeset -n ref=var; ref=bar fun
export var=bar
typeset -g -n ref=var
var=bar
ref=bar
In the function "fun", the environment implied by the parameters "var" and
"ref" only contains the entry "var"->"bar" but in reality the
"precomputed" environment would contain the two entries "var"->"bar" and
"ref"->"bar".
Repurposing the precomputed environment into an entity of its own is
technically possible but it doesn't look wise to me. I would rather avoid
that route.
Philippe
--0000000000004b939f0656aabd53
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><div class=3D"gmail_quote gmail_quote_container"><blockquo=
te class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:1px =
solid rgb(204,204,204);padding-left:1ex"><div dir=3D"ltr"><div class=3D"gma=
il_quote"><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8=
ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">In my opinion, =
an assignment before a command should be the same as a<br>
normal assignment as far as possible with the addition of adding the<br>
export of the explicitly named variable.<br></blockquote><div><br></div><di=
v>In principle, I agree with these requirements however I think that there =
is an even more imperative requirement, namely that=C2=A0<span style=3D"bac=
kground-color:transparent">"cmd" must run in exactly the same env=
ironment when run with "ref=3Dfoo cmd" as when run with "fun=
() { cmd }; ref=3Dfoo fun". This requirement puts severe constraints o=
n what's possible. If you consider only the case=C2=A0</span><span styl=
e=3D"background-color:transparent">"ref=3Dfoo cmd", then you coul=
d modify the implementation of inline assignments for example to augment th=
e environment with the pair "ref"->"foo" just after =
the call to addvars and before the execution of "cmd". However, n=
othing similar is possible for the case=C2=A0</span><span style=3D"backgrou=
nd-color:transparent">"fun() { cmd }; ref=3Dfoo fun". Here, your =
only levers are to modify the parameter table that will be visible to "=
;fun" in such a way that when "fun" executes "cmd"=
, the environment will be the desired one.</span></div><div><span style=3D"=
background-color:transparent"><br></span></div><div><span style=3D"backgrou=
nd-color:transparent">For our discussion we should consider the following e=
xample:</span></div><div><span style=3D"background-color:transparent"><br><=
/span></div><div><span style=3D"background-color:transparent">var=3Dfoo</sp=
an></div><div><span style=3D"background-color:transparent">typeset -n ref=
=3Dvar</span></div><div><span style=3D"background-color:transparent">fun() =
{</span></div><div><span style=3D"background-color:transparent">=C2=A0 type=
set -p var ref</span></div><div><span style=3D"background-color:transparent=
">=C2=A0 printenv var</span></div><div><span style=3D"background-color:tran=
sparent">=C2=A0 printenv ref</span></div><div><span style=3D"background-col=
or:transparent">}</span></div><div><span style=3D"background-color:transpar=
ent">ref=3Dbar fun</span></div><div><span style=3D"background-color:transpa=
rent"><br></span></div><div><span style=3D"background-color:transparent">Wh=
at we need to answer is what should the "typeset -p" and the two =
"printenv" print. What the implementation of "</span><span s=
tyle=3D"background-color:transparent">ref=3Dbar fun" can do is change =
the value of ref and/or var and modify their flags. We can also decide whet=
her -x is allowed on references and what it has for effect. We could possib=
ly also define one or more new parameter flags (although there are only ver=
y few PM bits left).</span></div></div></div></blockquote><div><br></div><d=
iv>Technically, it would be possible to go beyond by (ab)using the precompu=
ted environment but I don't think this would be wise.</div><div><br></d=
iv><div>My understanding of the Zsh language is that whenever an external c=
ommand is run (with no inline assignments), it is expected to run in an env=
ironment=C2=A0that is fully specified by the set of -x flagged parameters p=
resent in the scope in which the command is run; an entry N->V is presen=
t in that=C2=A0<span style=3D"background-color:transparent">environment if =
and only if=C2=A0</span>the scope of the command contains=C2=A0<span style=
=3D"background-color:transparent">an -x flagged parameter named N and the s=
tring representation of its value is the string V.</span></div><div><span s=
tyle=3D"background-color:transparent"><br></span></div><div><span style=3D"=
background-color:transparent">This can be implemented by computing, each ti=
me an external command is run, a=C2=A0new=C2=A0</span><span style=3D"backgr=
ound-color:transparent">environment that=C2=A0contains=C2=A0</span><span st=
yle=3D"background-color:transparent">the names and values of all the -X fla=
gged parameters visible at that moment. T</span><span style=3D"background-c=
olor:transparent">he current implementation of Zsh optimizes this by mainta=
ining a precomputed environment.</span><span style=3D"background-color:tran=
sparent">=C2=A0</span><span style=3D"background-color:transparent">Each tim=
e a parameter is flagged with -x, a corresponding entry is added to the=C2=
=A0</span><span style=3D"background-color:transparent">precomputed environm=
ent and each time the value of an -x flagged parameter is modified, the cor=
responding entry is updated. Whenever an external command is run, the=C2=A0=
</span><span style=3D"background-color:transparent">precomputed environment=
already contains all the expected entries and can be used as the=C2=A0</sp=
an><span style=3D"background-color:transparent">environment for the command=
.</span></div><div><br></div><div><span style=3D"background-color:transpare=
nt">Oliver stated that he expects the following:</span></div><div><span sty=
le=3D"background-color:transparent"><br></span></div><blockquote class=3D"g=
mail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204=
,204,204);padding-left:1ex">export var=3Dfoo; typeset -n ref=3Dvar;=C2=A0 =
=C2=A0 ref=3Dbar env - export ref=3Dbar var=3Dbar</blockquote><div><br></di=
v><div>This implies that the inline assignment "ref=3Dbar" should=
A) assign "bar" to the parameter referred by "ref", he=
re "var" and B) add the entry "ref"->"bar"=
to the environment of the inline command. This could be achieved by execut=
ing "ref=3Dbar" as a normal assignment and then adding the entry=
=C2=A0"ref"->"bar" to the precomputed environment. T=
he consequence would be that the "precomputed"=C2=A0<span style=
=3D"background-color:transparent">environment would no longer simply be a p=
recomputation of the environment implied by the -x flagged parameters.=C2=
=A0</span>It would instead become an entity of its own.</div><div><span sty=
le=3D"background-color:transparent"><br></span></div><div><span style=3D"ba=
ckground-color:transparent">Consider the following example:</span></div><di=
v><br></div><div><span style=3D"background-color:transparent"><font face=3D=
"monospace">% fun() { typeset -p var ref; echo var=3D$(printenv var); echo =
ref=3D$(printenv ref) }</font></span></div><div><font face=3D"monospace"><s=
pan style=3D"background-color:transparent">%=C2=A0</span><span style=3D"bac=
kground-color:transparent">export var=3Dfoo; typeset -n ref=3Dvar; ref=3Dba=
r fun</span></font></div><div><font face=3D"monospace">export var=3Dbar<br>=
typeset -g -n ref=3Dvar<br>var=3Dbar<br>ref=3Dbar</font></div><div><br></di=
v><div>In the function "fun", the environment implied by the para=
meters "var" and "ref" only contains the entry "va=
r"->"bar" but in reality=C2=A0the "precomputed"=
=C2=A0environment would contain the two entries=C2=A0"var"->&q=
uot;bar" and "ref"->"bar".<br><br></div><div>Re=
purposing the=C2=A0precomputed=C2=A0environment into an entity of its own i=
s technically possible but it doesn't look wise to me. I would rather a=
void that route.</div><div><br></div><div>Philippe</div><div><br></div></di=
v></div>
--0000000000004b939f0656aabd53--