• Bash ${!word} computed variable syntax.

    From Kaz Kylheku@21:1/5 to All on Tue Jun 6 03:32:44 2023
    Curious situation.

    Bash has a computed variable syntax. If there is a $word variable which
    holds foo, then ${!word} will produce expansion $foo.

    The only mention of this syntax is in an Appendix in the GNU Info
    documentation which lists differences between Bash and The Bourne Shell.

    There a single bullet point is made:

    ] * Bash has indirect variable expansion using ${!word} (see Shell
    ] Parameter Expansion).

    But there is no mention of it in Shell Parameter Expansion.

    The other {! gadgets are documented like ${!name[@]} and ${!pattern@}
    and whatnot; but not that one.

    The man page mentions nothing about it at all.

    More curiously, this doc situation already exists in the oldest commit
    in Bash git, which was an import of bash-2.0 sources dated 1996.
    (I'm presuming that the feature worked then also.)

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kenny McCormack@21:1/5 to 864-117-4973@kylheku.com on Tue Jun 6 04:24:19 2023
    In article <20230605202611.371@kylheku.com>,
    Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    Curious situation.

    Bash has a computed variable syntax. If there is a $word variable which
    holds foo, then ${!word} will produce expansion $foo.

    The only mention of this syntax is in an Appendix in the GNU Info >documentation which lists differences between Bash and The Bourne Shell.

    (Assuming the point of your post is: Can you tell me where this is
    documented?)

    I found (in "man bash"):

    ${parameter}
    The value of parameter is substituted. The braces are required
    when parameter is a positional parameter with more than one
    digit, or when parameter is followed by a character which is not
    to be interpreted as part of its name. The parameter is a shell
    parameter as described above PARAMETERS) or an array reference
    (Arrays).

    If the first character of parameter is an exclamation point (!), it
    introduces a level of variable indirection. Bash uses the value of the
    variable formed from the rest of parameter as the name of the variable;
    this variable is then expanded and that value is used in the rest of the
    substitution, rather than the value of parameter itself. This is known
    as indirect expansion. The exceptions to this are the expansions of
    ${!prefix*} and ${!name[@]} described below. The exclamation point must
    immediately follow the left brace in order to introduce indirection.

    --
    A racist, a Nazi, and a Klansman walk into a bar...

    Bartender says, "What will it be, Mr. Trump?"

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David W. Hodgins@21:1/5 to Kaz Kylheku on Tue Jun 6 00:18:41 2023
    On Mon, 05 Jun 2023 23:32:44 -0400, Kaz Kylheku <864-117-4973@kylheku.com> wrote:

    Curious situation.

    Bash has a computed variable syntax. If there is a $word variable which
    holds foo, then ${!word} will produce expansion $foo.

    The only mention of this syntax is in an Appendix in the GNU Info documentation which lists differences between Bash and The Bourne Shell.

    There a single bullet point is made:

    ] * Bash has indirect variable expansion using ${!word} (see Shell
    ] Parameter Expansion).

    But there is no mention of it in Shell Parameter Expansion.

    The other {! gadgets are documented like ${!name[@]} and ${!pattern@}
    and whatnot; but not that one.

    The man page mentions nothing about it at all.

    More curiously, this doc situation already exists in the oldest commit
    in Bash git, which was an import of bash-2.0 sources dated 1996.
    (I'm presuming that the feature worked then also.)

    From the section Parameter Expansion of man bash ...
    If the first character of parameter is an exclamation point (!), and parameter is not a nameref, it introduces a level of indirection. Bash uses the value formed by expanding the rest of parameter
    as the new parameter; this is then expanded and that value is used in the rest of the expansion, rather than the expansion of the original parameter. This is known as indirect expansion. The
    value is subject to tilde expansion, parameter expansion, command substitution, and arithmetic expansion. If parameter is a nameref, this expands to the name of the parameter referenced by parame‐
    ter instead of performing the complete indirect expansion. The exceptions to this are the expansions of ${!prefix*} and ${!name[@]} described below. The exclamation point must immediately follow
    the left brace in order to introduce indirection.

    That's with GNU bash, version 5.1.16(1)-release

    Regards, Dave Hodgins

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Javier@21:1/5 to Kaz Kylheku on Wed Jun 7 18:39:34 2023
    Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    Bash has a computed variable syntax. If there is a $word variable which
    holds foo, then ${!word} will produce expansion $foo.

    BTW, does anybody know why the shell does not allow the nested
    brace syntax ${${variable}} for indirect variable reference?

    $ a=b
    $ b=c
    $ echo ${${a}}
    -bash: ${${a}}: bad substitution

    It would make sense, as it is the most intuitive syntax.
    In perl the nested brace syntax works alright

    $ perl -e '${a}="b"; ${b}="c"; print "${${a}}\n"'
    c

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Christian Weisgerber@21:1/5 to Javier on Wed Jun 7 19:25:36 2023
    On 2023-06-07, Javier <invalid@invalid.invalid> wrote:

    BTW, does anybody know why the shell does not allow the nested
    brace syntax ${${variable}} for indirect variable reference?

    Presumably because it does a single pass of variable substitutions.
    The result of a variable substitution is not subject to variable
    substitution again.

    --
    Christian "naddy" Weisgerber naddy@mips.inka.de

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Christian Weisgerber on Wed Jun 7 20:39:29 2023
    On 2023-06-07, Christian Weisgerber <naddy@mips.inka.de> wrote:
    On 2023-06-07, Javier <invalid@invalid.invalid> wrote:

    BTW, does anybody know why the shell does not allow the nested
    brace syntax ${${variable}} for indirect variable reference?

    Presumably because it does a single pass of variable substitutions.
    The result of a variable substitution is not subject to variable
    substitution again.

    But that would still be the case here. If the inner ${variable}
    happened to produce the text "foo$a" that would no longer be
    scanned to replace $a; it would be inserted as ${foo$a}, and
    resolved by looking for the variable name foo$a. (Or indicating
    an error due to a bad variable name.)

    Note that expansions *are* performed in some ${...} syntaxes.
    For isntance in ${foo%bar}, bar is subject to substitutions!

    Long-winded way to do ${TERM%r}:

    $ echo $TERM
    screen-256color
    $ echo ${TERM%$(echo -n R | tr '[A-Z]' '[a-z]')}
    screen-256colo

    In principle, the left hand side of % could also be processed
    for expansions in the same way. Thus, the whole interior of
    ${...} when there is no % operator.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Javier on Wed Jun 7 20:34:21 2023
    On 2023-06-07, Javier <invalid@invalid.invalid> wrote:
    Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    Bash has a computed variable syntax. If there is a $word variable which
    holds foo, then ${!word} will produce expansion $foo.

    BTW, does anybody know why the shell does not allow the nested
    brace syntax ${${variable}} for indirect variable reference?

    That's exactly why I was investigating into it.

    The feature is present, together with arrays and ${name[@]} and all
    that in bash-2.0.

    The feature is absent, as are arrays and all, in bash-1.14.17.

    The GNU FTP archive has no tarball of Bash between those two
    versions!

    I was trying to find out whether ${!word} was introduced before,
    after, or at the same time as ${!name[@]} and those other ${!...}
    notations.

    So, as far as I can guess, Ramey (or someone) perhaps introduced a ${!
    sequence for signaling the start of some expansion extensions. Computed
    names ended up there, whether as a first feature, or later one.

    To learn more, we would have to bother Ramey with questions.

    $ a=b
    $ b=c
    $ echo ${${a}}
    -bash: ${${a}}: bad substitution

    Consider that this would be crippled if it only supported ${$.

    Programmers would expect it to work like computed variables in Make,
    which supports:

    OBJS_$(this_file) = foo.o

    Such that you can then reference:

    $(OBJS_$(file))

    People would try ${abc_$foo} and expect that to work too,
    not just ${$foo}.

    Whereas ${!...} can more easily handwaved away as the ${! being
    a canned sequence for introducing a bunch of extensions (that
    are not even related).

    It would make sense, as it is the most intuitive syntax.
    In perl the nested brace syntax works alright

    $ perl -e '${a}="b"; ${b}="c"; print "${${a}}\n"'

    Possibly, it adds a cost. The shell implementation would have to scan
    the interior of every simple ${var} reference for dollar sign
    expansions. Rather than just accumulating the characters and pumping the resulting identifier through a hash table.

    (That cost can be addressed by compiling the notation: doing the
    scan when the syntax is processed, not every time the surrounding
    function is called or loop body executed. So it's not an entirely
    valid objection; but it does somewhat raise the bar for a good
    implementation.)

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Kaz Kylheku on Thu Jun 8 00:38:20 2023
    On 07.06.2023 22:34, Kaz Kylheku wrote:
    On 2023-06-07, Javier <invalid@invalid.invalid> wrote:
    Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    Bash has a computed variable syntax. If there is a $word variable which
    holds foo, then ${!word} will produce expansion $foo.

    BTW, does anybody know why the shell does not allow the nested
    brace syntax ${${variable}} for indirect variable reference?

    Because it's non-POSIX ? - Normative text is spread across the
    specs, you'll probably have to read the whole chapter 2.6 on
    Word Expansion, but see also 'parameter' vs. 'word' definitions
    in the syntax.


    That's exactly why I was investigating into it.

    The feature is present, together with arrays and ${name[@]} and all
    that in bash-2.0.

    The feature is absent, as are arrays and all, in bash-1.14.17.

    The GNU FTP archive has no tarball of Bash between those two
    versions!

    I was trying to find out whether ${!word} was introduced before,
    after, or at the same time as ${!name[@]} and those other ${!...}
    notations.

    All I can say is that 'typeset -n nameref' and '${!nameref}' were
    introduced with ksh93 at times when bash did not support namerefs.
    The same with variable patterns, e.g. as in ${!HIST*}, or the
    array subscripts ${!arr[@]} etc.


    So, as far as I can guess, Ramey (or someone) perhaps introduced a ${! sequence for signaling the start of some expansion extensions. Computed names ended up there, whether as a first feature, or later one.

    I _think_ (but may be misremembering) that bash supported ${!var}
    before it supported namerefs, and after ksh used that syntax. But
    ksh does not support the indirection ${!var} with bash semantic.
    So most of the ${!...} semantics seems to have been borrowed by
    bash, with the exception of the [non-nameref] var-indirection.


    To learn more, we would have to bother Ramey with questions.

    [...]

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Javier@21:1/5 to Kaz Kylheku on Wed Jun 7 23:55:57 2023
    Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    $ a=b
    $ b=c
    $ echo ${${a}}
    -bash: ${${a}}: bad substitution

    Consider that this would be crippled if it only supported ${$.

    ...

    People would try ${abc_$foo} and expect that to work too,
    not just ${$foo}.

    Another problem with the ${abc_${foo}} syntax would be that it would
    complicate the specification for typing variables with the NAMEREF
    attribute in the builtin 'declare'

    declare: declare [-aAfFgilnrtux] [-p] [name[=value] ...]
    -n make NAME a reference to the variable named by its value

    Whereas ${!...} can more easily handwaved away as the ${! being
    a canned sequence for introducing a bunch of extensions (that
    are not even related).

    The choice of syntax for those extensions doesn't look coherent.
    ${!var} is for getting variable contents, while ${!var*} is for
    getting variable names. I wouldn't call that a good syntax design.

    '${!PREFIX*}'
    '${!PREFIX@}'
    Expands to the names of variables whose names begin with PREFIX,
    ...

    '${!NAME[@]}'
    '${!NAME[*]}'
    If NAME is an array variable, expands to the list of array indices
    (keys) assigned in NAME.
    ...

    In any case the syntax seems to come from the ksh designers, not Chet Ramey.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Janis Papanagnou on Wed Jun 7 23:53:35 2023
    On 2023-06-07, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 07.06.2023 22:34, Kaz Kylheku wrote:
    On 2023-06-07, Javier <invalid@invalid.invalid> wrote:
    Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    Bash has a computed variable syntax. If there is a $word variable which >>>> holds foo, then ${!word} will produce expansion $foo.

    BTW, does anybody know why the shell does not allow the nested
    brace syntax ${${variable}} for indirect variable reference?

    Because it's non-POSIX ? - Normative text is spread across the
    specs, you'll probably have to read the whole chapter 2.6 on
    Word Expansion, but see also 'parameter' vs. 'word' definitions
    in the syntax.

    Do you meant that supporting expansions in the variable name
    would violate POSIX (be a non-conforming extension that would have
    to be disabled in POSIX mode?)

    Even that wouldn't necessarily stop it, unless it broke a lot of
    code in practice.

    If ${a$b} happens to be not well-formed according to POSIX,
    then the only thing wrong with making it work is that we are
    not loudly diagnosing the error.

    So, as far as I can guess, Ramey (or someone) perhaps introduced a ${!
    sequence for signaling the start of some expansion extensions. Computed
    names ended up there, whether as a first feature, or later one.

    I _think_ (but may be misremembering) that bash supported ${!var}
    before it supported namerefs

    This is correct. The bash-2.0 archive has ${!var}, as I mentioned.

    Furthermore, it has this:

    14) Which new features in ksh-93 are not in bash, and which are?

    New things in ksh-93 not in bash-2.0:
    associative arrays
    floating point arithmetic
    ++, --, comma arithmetic operators
    math library functions
    ${!name[sub]} name of subscript for associative array
    ${!prefix*} and {!prefix@} variable name prefix expansions
    `.' is allowed in variable names to create a hierarchical namespace
    more extensive compound assignment syntax
    discipline functions
    `sleep' and `getconf' builtins (bash has loadable versions)
    ***** typeset -n and `nameref' variables *****

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Javier on Thu Jun 8 03:03:59 2023
    On 08.06.2023 02:38, Javier wrote:
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 07.06.2023 22:34, Kaz Kylheku wrote:
    On 2023-06-07, Javier <invalid@invalid.invalid> wrote:
    Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    Bash has a computed variable syntax. If there is a $word variable which >>>>> holds foo, then ${!word} will produce expansion $foo.

    BTW, does anybody know why the shell does not allow the nested
    brace syntax ${${variable}} for indirect variable reference?

    Because it's non-POSIX ? - Normative text is spread across the
    specs, you'll probably have to read the whole chapter 2.6 on
    Word Expansion, but see also 'parameter' vs. 'word' definitions
    in the syntax.

    Supporting the ${${variable}} syntax would not stop Bash from being
    able to execute correctly POSIX shell code. POSIX-compliant shells
    can implement non-POSIX extensions, and even exists the possibility
    that those extensions be adopted by POSIX in the future.

    What I meant was that in POSIX there's some definitions what may
    and what may not appear in lexical context - that's what I thought
    to have seen in chapter 2.6 -, and I did not mean any lexically
    non-related extensions.

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Javier@21:1/5 to Janis Papanagnou on Thu Jun 8 00:45:08 2023
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    Because it's non-POSIX ?

    Also, since you mention non-POSIXness, I checked whether the nested
    variable reference syntax is supported in zsh, which does not attempt
    to be POSIX compliant.

    To my surprise this is what it happens in zsh:

    a="b"; b="c"; printf "${${a}}\n";
    b

    I checked the zsh docs and it needs a parameter expansion flag (P) to
    get the expected result:

    a="b"; b="c"; printf "${(P)${a}}\n";
    c

    This looks like an awful syntax design in zsh, and can lead to very
    hard to detect bugs, especially for people familiar with perl

    perl -e '${a}="b"; ${b}="c"; print "${${a}}\n"'
    c

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Javier on Thu Jun 8 00:18:53 2023
    On 2023-06-07, Javier <invalid@invalid.invalid> wrote:
    Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    $ a=b
    $ b=c
    $ echo ${${a}}
    -bash: ${${a}}: bad substitution

    Consider that this would be crippled if it only supported ${$.

    ...

    People would try ${abc_$foo} and expect that to work too,
    not just ${$foo}.

    Another problem with the ${abc_${foo}} syntax would be that it would complicate the specification for typing variables with the NAMEREF
    attribute in the builtin 'declare'

    declare: declare [-aAfFgilnrtux] [-p] [name[=value] ...]
    -n make NAME a reference to the variable named by its value

    I don't see the huge issue. You calculate the name like abc_$foo ->
    abc_xyz. Then if abc_xyz is declared as a nameref, you dereference it;
    that part is basically independent and transparent.

    Bash's ${!word} specification makes a big deal about whether word itself
    is a nameref. The behavior splits. If it's not a nameref, you get the
    named variable indirection. If word it is a nameref, then the feature
    retrieves the name of the variable targeted by word. Completely
    unrelated, different semantics, ouch.

    Nothing is mentioned about the case when the target variable of {$!word}
    is a nameref (and $word isn't); presumably that works. We dereference to
    the nameref, which then dereferences to its target, the way we
    would want ${abc_$foo} to work.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Javier@21:1/5 to Janis Papanagnou on Thu Jun 8 00:38:22 2023
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 07.06.2023 22:34, Kaz Kylheku wrote:
    On 2023-06-07, Javier <invalid@invalid.invalid> wrote:
    Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    Bash has a computed variable syntax. If there is a $word variable which >>>> holds foo, then ${!word} will produce expansion $foo.

    BTW, does anybody know why the shell does not allow the nested
    brace syntax ${${variable}} for indirect variable reference?

    Because it's non-POSIX ? - Normative text is spread across the
    specs, you'll probably have to read the whole chapter 2.6 on
    Word Expansion, but see also 'parameter' vs. 'word' definitions
    in the syntax.

    Supporting the ${${variable}} syntax would not stop Bash from being
    able to execute correctly POSIX shell code. POSIX-compliant shells
    can implement non-POSIX extensions, and even exists the possibility
    that those extensions be adopted by POSIX in the future.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Kaz Kylheku on Thu Jun 8 03:14:34 2023
    On 08.06.2023 02:18, Kaz Kylheku wrote:

    Bash's ${!word} specification makes a big deal about whether word itself
    is a nameref. The behavior splits. If it's not a nameref, you get the
    named variable indirection. If word it is a nameref, then the feature retrieves the name of the variable targeted by word. Completely
    unrelated, different semantics, ouch.

    Indeed. - What they seem to try to do is finding any non-conflicting combination of punctuation characters to implement new features. (A
    bit of a cynical view, I admit.)

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Javier@21:1/5 to Kaz Kylheku on Thu Jun 8 01:19:00 2023
    Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    Bash's ${!word} specification makes a big deal about whether word itself
    is a nameref. The behavior splits. If it's not a nameref, you get the
    named variable indirection. If word it is a nameref, then the feature retrieves the name of the variable targeted by word. Completely
    unrelated, different semantics, ouch.

    You are right, the behavior is different and I wasn't aware of it until
    I read your message and tested the behavior myself with an example:

    $ a=b; b=c; printf "${!a}\n";
    c

    $ declare -n a=b; b=c; printf "${!a}\n";
    b

    It's an awful design for a shell feature that was intended to prevent bugs. Instead of preventing bugs it creates them.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Javier@21:1/5 to Janis Papanagnou on Thu Jun 8 02:05:20 2023
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 08.06.2023 02:38, Javier wrote:
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 07.06.2023 22:34, Kaz Kylheku wrote:
    On 2023-06-07, Javier <invalid@invalid.invalid> wrote:
    Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    Bash has a computed variable syntax. If there is a $word variable which >>>>>> holds foo, then ${!word} will produce expansion $foo.

    BTW, does anybody know why the shell does not allow the nested
    brace syntax ${${variable}} for indirect variable reference?

    Because it's non-POSIX ? - Normative text is spread across the
    specs, you'll probably have to read the whole chapter 2.6 on
    Word Expansion, but see also 'parameter' vs. 'word' definitions
    in the syntax.

    Supporting the ${${variable}} syntax would not stop Bash from being
    able to execute correctly POSIX shell code. POSIX-compliant shells
    can implement non-POSIX extensions, and even exists the possibility
    that those extensions be adopted by POSIX in the future.

    What I meant was that in POSIX there's some definitions what may
    and what may not appear in lexical context - that's what I thought
    to have seen in chapter 2.6 -, and I did not mean any lexically
    non-related extensions.

    https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/utilities/V3_chap02.html#tag_18_06

    I don't see anything in section 2.6 forbidding the nested ${${variable}} syntax.
    Also, the ${!word} does not appear in the POSIX specification.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Keith Thompson@21:1/5 to Kaz Kylheku on Wed Jun 7 22:21:20 2023
    Kaz Kylheku <864-117-4973@kylheku.com> writes:
    On 2023-06-07, Javier <invalid@invalid.invalid> wrote:
    Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    Bash has a computed variable syntax. If there is a $word variable which
    holds foo, then ${!word} will produce expansion $foo.

    BTW, does anybody know why the shell does not allow the nested
    brace syntax ${${variable}} for indirect variable reference?

    That's exactly why I was investigating into it.

    The feature is present, together with arrays and ${name[@]} and all
    that in bash-2.0.

    The feature is absent, as are arrays and all, in bash-1.14.17.

    The GNU FTP archive has no tarball of Bash between those two
    versions!

    I was trying to find out whether ${!word} was introduced before,
    after, or at the same time as ${!name[@]} and those other ${!...}
    notations.
    [...]

    This answer on unix.stackexchange.com:
    https://unix.stackexchange.com/a/403119/10454
    includes links to some older bash tarballs that aren't on ftp.gnu.org.

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    Will write code for food.
    void Void(void) { Void(); } /* The recursive call of the void */

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Janis Papanagnou on Thu Jun 8 17:41:11 2023
    On 2023-06-08, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 08.06.2023 02:18, Kaz Kylheku wrote:

    Bash's ${!word} specification makes a big deal about whether word itself
    is a nameref. The behavior splits. If it's not a nameref, you get the
    named variable indirection. If word it is a nameref, then the feature
    retrieves the name of the variable targeted by word. Completely
    unrelated, different semantics, ouch.

    Indeed. - What they seem to try to do is finding any non-conflicting combination of punctuation characters to implement new features. (A
    bit of a cynical view, I admit.)

    But in this case it's totally conflicting at that leverl:
    ${!word} versus ... ${!word}. The behavvior is distinguished by a
    declared property of word, which, I think, in the worst case, is not
    knowable until ${!word} is being executed at run-time.

    ${!word} could occur in a Bash function, which is followed by
    a dot command that source a file which declares a nameref,
    and then that function is called and must treat word accordingly.
    (Right?)

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Javier on Thu Jun 8 17:47:32 2023
    On 2023-06-08, Javier <invalid@invalid.invalid> wrote:
    This looks like an awful syntax design in zsh, and can lead to very
    hard to detect bugs, especially for people familiar with perl

    perl -e '${a}="b"; ${b}="c"; print "${${a}}\n"'
    c

    WHy don't you ask in the Bash mailing list: if someone made a
    good quality patch to implement ${abc_${a}}, with documentation
    and test cases, would they merge it?

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Keith Thompson@21:1/5 to Kaz Kylheku on Thu Jun 8 11:54:30 2023
    Kaz Kylheku <864-117-4973@kylheku.com> writes:
    Curious situation.

    Bash has a computed variable syntax. If there is a $word variable which
    holds foo, then ${!word} will produce expansion $foo.
    [...]

    Something the manual doesn't directly address (or I've missed it) is
    that a nameref can refer to a nameref, to arbitrarily many levels.

    For example:
    ```
    #!/bin/bash

    declare -n var0=var1
    declare -n var1=var2
    declare -n var2=var3
    declare -n var3=var4
    declare -n var4=var5
    var5=42

    echo "var0=$var0"
    echo "\${!var0}=${!var0}"
    ```

    The output is:
    ```
    var0=42
    ${!var0}=var5
    ```

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    Will write code for food.
    void Void(void) { Void(); } /* The recursive call of the void */

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Martijn Dekker@21:1/5 to All on Thu Jun 8 22:32:26 2023
    Op 07-06-2023 om 19:39 schreef Javier:
    BTW, does anybody know why the shell does not allow the nested
    brace syntax ${${variable}} for indirect variable reference?

    The abandoned 93v- beta of AT&T ksh93 (and hence also the derived and
    equally abandoned AT&T ksh2020 project) supported a ${$variable} syntax
    for indirection, though nested braces are a syntax error.

    I've found multiple bugs in their implementation of it, the worst being:

    - It malfunctions for indexed array subscripts other than 0, @ and *
    (e.g., ${$var[2]}) and associative array subscripts other than @ and *
    - It crashes when the variable name is a negative integer

    But those problems might be fixable. I could try backporting and fixing
    it for a future ksh 93u+m/1.1 release.

    However, the functionality is redundant because namerefs already exist:

    $ nameref foo=bar
    $ bar=OK
    $ echo $foo
    OK

    The new syntax would allow:

    $ foo=bar
    $ bar=OK
    $ echo ${$foo}
    OK

    It takes just as many steps, so I don't really see the advantage.

    --
    || modernish -- harness the shell
    || https://github.com/modernish/modernish
    ||
    || KornShell lives!
    || https://github.com/ksh93/ksh

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Martijn Dekker on Fri Jun 9 01:51:51 2023
    On 2023-06-08, Martijn Dekker <martijn@inlv.demon.nl> wrote:
    Op 07-06-2023 om 19:39 schreef Javier:
    BTW, does anybody know why the shell does not allow the nested
    brace syntax ${${variable}} for indirect variable reference?

    The abandoned 93v- beta of AT&T ksh93 (and hence also the derived and
    equally abandoned AT&T ksh2020 project) supported a ${$variable} syntax
    for indirection, though nested braces are a syntax error.

    I've found multiple bugs in their implementation of it, the worst being:

    - It malfunctions for indexed array subscripts other than 0, @ and *
    (e.g., ${$var[2]}) and associative array subscripts other than @ and *
    - It crashes when the variable name is a negative integer

    But those problems might be fixable. I could try backporting and fixing
    it for a future ksh 93u+m/1.1 release.

    However, the functionality is redundant because namerefs already exist:

    $ nameref foo=bar
    $ bar=OK
    $ echo $foo
    OK

    The new syntax would allow:

    $ foo=bar
    $ bar=OK
    $ echo ${$foo}
    OK

    It takes just as many steps, so I don't really see the advantage.

    The advantage would be Make style computed names that aren't pure
    name indirections: ${abc_${foo}} and such.

    In Make, you can use that to construct information databases.
    By using dot in a name, it looks like member referencing.

    Let's use points: everyone's favorite example for structures.

    p1.x = 0
    p1.y = 1

    p2.x = 2
    p2.y = 3


    Now if we have a variable $(P) that holds a point like p1 or p2,
    we can do $($(P).x) and $($(P).y) to read the "fields".

    If symbols can be machine generated, then eval-ed assignments
    can simulate real dynamic objects.

    point=$(gensym) # point takes on value like __g0037

    eval "${point}_x=42" # evaluates __g0037_x=0

    echo ${${gensym}_x} # prints 42


    Maybe if variable interpolations are allowed in variable names,
    they should be allowed in assignments too (in the left hand
    side) without eval, just:

    ${point}_x=42

    Works in Make:

    OBJECTS_$(this_file) := foo.o # GNU Make eager assignment
    OBJECTS_$(this_file) += bar.o



    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Keith Thompson on Fri Jun 9 01:43:51 2023
    On 2023-06-08, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    Kaz Kylheku <864-117-4973@kylheku.com> writes:
    Curious situation.

    Bash has a computed variable syntax. If there is a $word variable which
    holds foo, then ${!word} will produce expansion $foo.
    [...]

    Something the manual doesn't directly address (or I've missed it) is
    that a nameref can refer to a nameref, to arbitrarily many levels.
    For example:
    ```
    #!/bin/bash

    declare -n var0=var1
    declare -n var1=var2
    declare -n var2=var3
    declare -n var3=var4
    declare -n var4=var5
    var5=42

    echo "var0=$var0"
    echo "\${!var0}=${!var0}"
    ```

    The output is:
    ```
    var0=42
    ${!var0}=var5

    The obvious question there is, how late is the binding?

    If I add this:

    declare -n var2=xvar3
    xvar3=73

    echo "var0=$var0"
    echo "\${!var0}=${!var0}"

    var0=73
    ${!var0}=xvar3

    You expect this because, look, the syntax is allowing you to bind
    references to nonexistent variables. The reference stores the
    name. References do not "slide" to the target variable.

    The second question is: could the observed behavior be
    produced, while the implementation is optimized.
    So that is to say, reference var0 could (perhaps at the time it
    is evaluated) cache the fact that it refers to var5, and
    then on subdsequent evaluations refer to the cache.

    "declare -n" statements could traverse the graph of
    known namerefs and invalidate these caches.

    Next question: how does this play with Bash's dynamic
    scope?

    One test is this: function-local override of
    namerefs.


    declare -n var0=var1
    declare -n var1=var2
    declare -n var2=var3
    declare -n var3=var4
    declare -n var4=var5
    var5=42


    out()
    {
    echo "var0=$var0"
    echo "\${!var0}=${!var0}"
    }

    fun()
    {
    local var2
    local xvar3=73
    declare -n var2=xvar3

    out
    }

    out
    fun
    out

    Output:

    var0=42
    ${!var0}=var5
    var0=73
    ${!var0}=xvar3
    var0=42
    ${!var0}=var5

    Well, that passed. When the function terminates, the dynamic
    scope is undone and the original nameref configuration is
    restored.

    Let's test something else. We have an ordinary non-nameref var0.
    In a dynamic scope, we create the var0->var1->...->var5 cascade.
    All those vars are declared local. What does var0 look like?

    When that scope terminates, what then?

    out()
    {
    echo "var0=$var0"
    echo "\${!var0}=${!var0}"
    }

    with_cascaded_namerefs()
    {
    local var0 var1 var2 var3 var4 var5
    declare -n var0=var1
    declare -n var1=var2
    declare -n var2=var3
    declare -n var3=var4
    declare -n var4=var5
    var5=42

    out
    }

    var0=ordinary

    out

    with_cascaded_namerefs

    out

    Output:

    var0=ordinary
    ${!var0}=
    var0=42
    ${!var0}=var5
    var0=ordinary
    ${!var0}=

    Also passed: var0 is overridden to nameref, then restored to ordinary.

    (If there aren't test cases like that, there should be.)

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Kaz Kylheku on Fri Jun 9 09:43:48 2023
    On 09.06.23 03:51, Kaz Kylheku wrote:

    Let's use points: everyone's favorite example for structures.

    p1.x = 0
    p1.y = 1

    p2.x = 2
    p2.y = 3


    Now if we have a variable $(P) that holds a point like p1 or p2,
    we can do $($(P).x) and $($(P).y) to read the "fields".

    In Kornshell we may write code like that (for example)

    typeset -T point=( x=0 y=0 )
    point p1=( x=1 y=2 )
    point p2=( x=3 y=4 )

    print $p1 $p2
    print ${p1.x} ${p1.y}
    print ${p2.x} ${p2.y}

    p1.x=5

    typeset -n p3=p1
    print $p3
    print ${p3.x} ${p3.y}



    producing this output

    ( x=1 y=2 ) ( x=3 y=4 )
    1 2
    3 4
    ( x=5 y=2 )
    5 2

    Accessing variables with dot-notation and namerefs for indirection.


    If symbols can be machine generated, then eval-ed assignments
    can simulate real dynamic objects.

    point=$(gensym) # point takes on value like __g0037

    eval "${point}_x=42" # evaluates __g0037_x=0

    echo ${${gensym}_x} # prints 42


    Maybe if variable interpolations are allowed in variable names,
    they should be allowed in assignments too (in the left hand
    side) without eval, just:

    ${point}_x=42

    For the latter (AFAICT) you'd need eval in Kornshell. But for the
    work with compound variables (as shown) it doesn't seem necessary.


    [...]

    Janis


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Javier@21:1/5 to Martijn Dekker on Sun Jun 11 00:10:51 2023
    Martijn Dekker <martijn@inlv.demon.nl> wrote:
    Op 07-06-2023 om 19:39 schreef Javier:
    BTW, does anybody know why the shell does not allow the nested
    brace syntax ${${variable}} for indirect variable reference?

    The abandoned 93v- beta of AT&T ksh93 (and hence also the derived and
    equally abandoned AT&T ksh2020 project) supported a ${$variable} syntax
    for indirection, though nested braces are a syntax error.

    I've found multiple bugs in their implementation of it

    From your response I see that somebody already tried to implement the
    feature in ksh. And it seems like the reason for abandoning it was to
    keep the parser implementation simple (which I am in favor of).

    One question: do shells use an intermediate bytecode compilation step
    as in lisp/python/perl? I'm guessing that might be one of the reasons
    for shells to keep very simple syntax rules.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Javier@21:1/5 to Kaz Kylheku on Wed Jun 14 01:48:10 2023
    Kaz Kylheku <864-117-4973@kylheku.com> wrote:
    The obvious question there is, how late is the binding?

    Apparently the nameref feature works differently in ksh and bash.
    I found this old message in the bash mailing list:

    Chet Ramey <chet.ramey@case.edu>
    Date: Sat, 15 Dec 2012 22:58:22
    Newsgroups: gnu.bash.bug
    Subject: Re: Requesting an alternate nameref feature

    On 12/12/12 1:04 PM, Dan Douglas wrote:

    > Hello. Could we possibly modify or create an additional variant of
    > "typeset -n" which produces "real" references rather than just
    > dynamic references by name? In other words, I'd like to be able
    > to create reference variables that always point to the instance of
    > a variable that was visible at the time the reference was created,
    > similar to the way ksh93's nameref works.

    This is the `pointer' implementation, as opposed to the `symbolic link'
    implementation I chose.

    The problem is the same as any use of pointers: what happens when the
    object you point to goes out of scope? Or would you restrict it, like
    declare -g, to only use the global scope?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)