Discussion:
About the "nameref" feature in bash...
(too old to reply)
Kenny McCormack
2024-12-08 12:51:05 UTC
Permalink
Bash has a "nameref" attribute on variables, that basically makes them like
"pointers" in languages like C. That is, the nameref variable has a value,
which is the name of another variable, and any/all references to the
nameref are, in fact, references to the other variable (with 2 exceptions
as discussed below). So, it works like this:

# Create a nameref variable and give it the value "foo"
declare -n MyNameRef=foo
foo=bar
echo $MyNameRef
(prints bar)
MyNameRef="Not bar"
echo $foo
(prints Not bar)

Now, the docs present this feature as being primarily useful with
functions. That is, something like:

foo() {
local -n MyNameRef=$1
# Function manipulates MyNameRef, but is, in fact, manipulating
# the variable whose name was passed as an arg to the function.
}

So, if we call foo as: foo bar
then references to MyNameRef inside foo() are, in fact, references to the
global variable "bar".

This is all well and good, but it leaves open the question of "How do I
assign a value to the nameref itself, given that any reference to the
nameref gets converted to a reference to whatever the nameref points to?"

The answer, as far as I can tell, is that there are exactly 2 exceptions to
the "always dereference" rule (for setting the value):
1) Use "declare" (or one of its synonyms, e.g., "local") to do the
assignment. This works, but is somewhat unappealing, since you may
end up declaring a variable more than once (which seems intuitively
wrong).
2) Use a "for" loop, like this:

for MyNameRef in one two three; do echo $MyNameRef; done

This magically assigns MyNameRef the values one, two, and three and
displays the values of those variables. Note that is different in
effect from the seemingly equivalent:

MyNameRef=one; echo $MyNameRef
MyNameRef=two; echo $MyNameRef
MyNameRef=three; echo $MyNameRef

Note: There is a syntax for reading the value of the nameref, but this
cannot be used to set the value. The syntax is: echo ${!MyNameRef}

In conclusion, my "reason for posting" is to confirm that I've got it right
that there are only 2 ways to set the value. Am I missing anything?
--
"We are in the beginning of a mass extinction, and all you can talk
about is money and fairy tales of eternal economic growth."

- Greta Thunberg -
Janis Papanagnou
2024-12-08 13:57:25 UTC
Permalink
Post by Kenny McCormack
Bash has a "nameref" attribute on variables,
Is that a newer feature of bash? (My version doesn't support -n with
'local' or with 'declare'.) My response is based on the ksh feature.
The underlying ideas of the feature might be reflected in both shells
in similar ways.
Post by Kenny McCormack
that basically makes them like
"pointers" in languages like C. That is, the nameref variable has a value,
which is the name of another variable, and any/all references to the
nameref are, in fact, references to the other variable (with 2 exceptions
# Create a nameref variable and give it the value "foo"
declare -n MyNameRef=foo
foo=bar
echo $MyNameRef
(prints bar)
MyNameRef="Not bar"
echo $foo
(prints Not bar)
Yes.
Post by Kenny McCormack
Now, the docs present this feature as being primarily useful with
foo() {
local -n MyNameRef=$1
# Function manipulates MyNameRef, but is, in fact, manipulating
# the variable whose name was passed as an arg to the function.
}
Yes.
Post by Kenny McCormack
So, if we call foo as: foo bar
then references to MyNameRef inside foo() are, in fact, references to the
global variable "bar".
Yes.
Post by Kenny McCormack
This is all well and good, but it leaves open the question of "How do I
assign a value to the nameref itself, given that any reference to the
nameref gets converted to a reference to whatever the nameref points to?"
If used in functions - where the actual function parameter from the
call cannot be changed from within the function ex post - it makes
sense to not "overwrite" the calling parameter.

I consider it as a reference (or name?) parameter, so it makes (as I
perceive it) no sense to redeclare/re-use it as a local variable, or
reassign a new name to it. - To emphasize my point; it's more like
the '&' in C++'s function parameter declaration. - But, that said,
it's possible [in ksh] to reassign it by another parameter(-name),
like

function foo {
typeset -n nr=$1
typeset -n nr=$2
nr=42
}

(where 'typeset -n' is the ksh equivalent of bash's form). That code
might make sense if there's some conditional between the two typeset.

A loop like

one=1 two=2 three=3
typeset -n nr
for nr in one two three
do (( nr = --count ))
done

would affect the variables one, two, and three, respectively. (This is
documented for ksh namerefs.)

After the loop 'nr' still points to variable 'three' and an assignment
would affect that variable. Re-assigning 'nr' to a new variable name
requires [in ksh] to explicitly re-declare it, e.g.

typeset -n nr=zero

(now 'nr' being quasi an alias-name to the variable 'zero').

Maybe some ideas here may be conveyed to the bash's implementation and
its behavior.

Janis
Post by Kenny McCormack
The answer, as far as I can tell, is that there are exactly 2 exceptions to
1) Use "declare" (or one of its synonyms, e.g., "local") to do the
assignment. This works, but is somewhat unappealing, since you may
end up declaring a variable more than once (which seems intuitively
wrong).
for MyNameRef in one two three; do echo $MyNameRef; done
This magically assigns MyNameRef the values one, two, and three and
displays the values of those variables. Note that is different in
MyNameRef=one; echo $MyNameRef
MyNameRef=two; echo $MyNameRef
MyNameRef=three; echo $MyNameRef
Note: There is a syntax for reading the value of the nameref, but this
cannot be used to set the value. The syntax is: echo ${!MyNameRef}
In conclusion, my "reason for posting" is to confirm that I've got it right
that there are only 2 ways to set the value. Am I missing anything?
Kenny McCormack
2024-12-08 14:41:31 UTC
Permalink
Post by Janis Papanagnou
Post by Kenny McCormack
Bash has a "nameref" attribute on variables,
Is that a newer feature of bash? (My version doesn't support -n with
'local' or with 'declare'.) My response is based on the ksh feature.
The underlying ideas of the feature might be reflected in both shells
in similar ways.
I think I read somewhere that it came in at bash 4.3 (c. 2014/2015) - which
is rather old now. So, I wouldn't call it "new".
--
Reading any post by Fred Hodgin, you're always faced with the choice of:
lunatic, moron, or troll.

I always try to be generous and give benefit of the doubt, by assuming troll.
Janis Papanagnou
2024-12-08 14:54:57 UTC
Permalink
Post by Kenny McCormack
Post by Janis Papanagnou
Post by Kenny McCormack
Bash has a "nameref" attribute on variables,
Is that a newer feature of bash? (My version doesn't support -n with
'local' or with 'declare'.) My response is based on the ksh feature.
The underlying ideas of the feature might be reflected in both shells
in similar ways.
I think I read somewhere that it came in at bash 4.3 (c. 2014/2015) - which
is rather old now. So, I wouldn't call it "new".
Yeah - "newer" is a very subjective and context specific term.

It's my [deliberately frozen] standard box I use regularly that
is still running bash 2.4; so some, erm.., "newer" features of
bash I cannot test [on that system] (without manual changes).
I see a downloaded "bash 5.0" package lying around on the file
system (not installed), but that's it. :-)

Janis
Kenny McCormack
2024-12-09 12:38:39 UTC
Permalink
Post by Janis Papanagnou
Post by Kenny McCormack
Post by Janis Papanagnou
Post by Kenny McCormack
Bash has a "nameref" attribute on variables,
Is that a newer feature of bash? (My version doesn't support -n with
'local' or with 'declare'.) My response is based on the ksh feature.
The underlying ideas of the feature might be reflected in both shells
in similar ways.
I think I read somewhere that it came in at bash 4.3 (c. 2014/2015) - which
is rather old now. So, I wouldn't call it "new".
Yeah - "newer" is a very subjective and context specific term.
Yup.
Post by Janis Papanagnou
It's my [deliberately frozen] standard box I use regularly that
is still running bash 2.4; so some, erm.., "newer" features of
bash I cannot test [on that system] (without manual changes).
I see a downloaded "bash 5.0" package lying around on the file
system (not installed), but that's it. :-)
I'm a fan of "retro" software, too, and I'm skeptical of the sort of people
you often see who act as if if you aren't running the absolute latest and
greatest, then your input is useless.

Most of my systems are running a pretty old spin of Debian and the bash
on these systems is of the 4.3 vintage.

OTOH, I am kind of a fanboy of bash and I do follow the developments there.
I usually self-compile the latest version for some subset of my systems. I
just d/l'd the 5.3.alpha tarball (which is from April 2024). Haven't
gotten around to compiling it yet. I'm wondering "how alpha is alpha?"
--
"Remember when teachers, public employees, Planned Parenthood, NPR and PBS
crashed the stock market, wiped out half of our 401Ks, took trillions in
TARP money, spilled oil in the Gulf of Mexico, gave themselves billions in
bonuses, and paid no taxes? Yeah, me neither."
Janis Papanagnou
2024-12-09 15:00:24 UTC
Permalink
Post by Kenny McCormack
[...]
I'm a fan of "retro" software, too, and I'm skeptical of the sort of people
you often see who act as if if you aren't running the absolute latest and
greatest, then your input is useless.
There's often some simplicity and good usability/ergonomy in older
[legacy] software that (strangely) a lot newer software, especially
GUI-based, is lacking.

I'm regularly verbally "attacked" by me not updating (for "security
reasons") all my systems to the newest version. (Thereby completely
disregarding all the problems [including security] you buy with new
versions.) But meanwhile they accepted my unconventional habits here.
Sadly, a lot of things just don't work with systems older than a few
years, especially Web/HTML based serviced.
Post by Kenny McCormack
[...] I'm wondering "how alpha is alpha?"
Concerning Bash questions I can just contribute what I heard by the
way, or by inference from Kornshell (which is in many features a
paragon for Bash features). Sorry.

Janis

Kenny McCormack
2024-12-09 13:57:55 UTC
Permalink
In article <vj48k5$3r1bq$***@dont-email.me>,
Janis Papanagnou <janis_papanagnou+***@hotmail.com> wrote:
...
Post by Janis Papanagnou
Post by Kenny McCormack
This is all well and good, but it leaves open the question of "How do I
assign a value to the nameref itself, given that any reference to the
nameref gets converted to a reference to whatever the nameref points to?"
If used in functions - where the actual function parameter from the
call cannot be changed from within the function ex post - it makes
sense to not "overwrite" the calling parameter.
To clarify, my actual use case does not involve functions at all. That's
why, in my OP, I made it clear that "the docs" talk mostly in terms of
using it with functions - implying that that was not my use case.

My use case is in mainline code, where I need to load up a bunch of
shell variables from a database. Like this:

mapfile -t < <(sqlite3 ...)
idx=0
for MyNameRef in var1 var2 var3 ...
do MyNameRef="${MAPFILE[idx++]}"
done

It all works fine; it all works just like the docs say it would.

I just find it a little strange; that's all. There is another (non-Unix)
scripting language with which I am familiar, where they implemented
"pointers" late-in-development; they followed the C idiom more closely,
where an unadorned instance of the pointer variable refers to the pointer
itself and to dereference it, you stick a * in front. This may be a little
more verbose, but it makes more sense to me.

Anyway, it seems clear at this point that the only 2 ways to assign to the
pointer itself (in "mainline code") are as described in the OP. I'm OK
with that.
--
1/20/17: A great day for all those people who are sick of being told
they don't know how to spell "you're" (or "there").
Loading...