Configuration Information [Automatically generated, do not change]:
Machine: x86_64
OS: cygwin
Compiler: gcc
Compilation CFLAGS: -g -O2
uname output: MSYS_NT-10.0-22631 ZackFramework16 3.6.4-dea57136.x86_64
2025-09-22 06:28 UTC x86_64 Msys
Machine Type: x86_64-pc-cygwin

Bash Version: 5.3
Patch Level: 0
Release Status: maint

Devel branch commit 6edfd0bf64

On Fri, Oct 17, 2025 at 3:35 PM Mark March <[email protected]> wrote:
>
> The ${var@A} expansion is defined as follows:
>
> > The expansion is a string in the form of an assignment statement or declare 
> > command that, if evaluated, recreates parameter with its attributes and 
> > value.
>
> Moreover, if the nameref references an array, the initializer in the declare 
> statement is incorrect even for the array variable referenced by the nameref: 
> it contains the first element of the array, not the entire array.

This is how this works without going through a nameref, too.

$ declare -a array=( zero one two three )
$ printf '%s\n' "${array@A}"
declare -a array='zero'
$ printf '%s\n' "${array[*]@A}"
declare -a array=([0]="zero" [1]="one" [2]="two" [3]="three")
$ printf '%s\n' "${array[@]@A}"
declare
-a
array=([0]="zero" [1]="one" [2]="two" [3]="three")
$ declare -p array
declare -a array=([0]="zero" [1]="one" [2]="two" [3]="three")

>From the Arrays section of the manual:
> Referencing an array variable without a subscript is equivalent to 
> referencing the array with a subscript of 0.
So you can kind of see where that came from, even if the output would
only allow you to correctly reproduce part of the array.

$ printf '%s\n' "${array[1]@A}"
declare -a array='one'
$ printf '%s\n' "${array[2]@A}"
declare -a array='two'

These wouldn't reproduce anything correctly. And contrast this:

$ declare -p 'array[0]'
bash: declare: array[0]: not found
$ declare -p 'array[1]'
bash: declare: array[1]: not found
$ declare -p 'array[2]'
bash: declare: array[2]: not found

Point is, if you're trying to use ${var@A} to reproduce an indexed or
associative array, you almost certainly want ${array[*]@A}.

> $ echo $BASH_VERSION
> 5.3.0(1)-release
>
> $ declare -a array=(1 2 3)
> $ declare -n nameref=array
>
> $ echo ${nameref@A}
> declare -a array='1'
>
> $ declare -p nameref
> declare -n nameref="array"

There's a lot to say about how a nameref interacts with an array in general.

$ declare -n nameref=array
$ printf '%s\n' "${nameref@A}"
declare -a array='zero'
$ printf '%s\n' "${nameref[*]@A}"
declare -a array=([0]="zero" [1]="one" [2]="two" [3]="three")
$ printf '%s\n' "${!nameref@A}"
declare -a array='zero'
$ printf '%s\n' "${!nameref[*]@A}"
bash: zero one two three: invalid variable name
$ printf '%s\n' "${!nameref[@]@A}"
bash: zero one two three: invalid variable name
$ printf '%s\n' "${!nameref}"
array
$ printf '%s\n' "${!nameref[1]@A}"

# Expands to nothing.
$ printf '%s\n' "${nameref[1]@A}"
declare -a array='one'
$ nameref=ten
$ declare -p array
declare -a array=([0]="ten" [1]="one" [2]="two" [3]="three")
# Assigned to index 0
$ nameref=( four five six seven )
$ declare -p array
declare -a array=([0]="four" [1]="five" [2]="six" [3]="seven")
# Compound assignment works.
$ nameref[2]=twelve
$ declare -p array
declare -a array=([0]="four" [1]="five" [2]="twelve" [3]="seven")
# Assignment to a specific index works.
$ declare -n nameref=array[1]
$ printf '%s\n' "${!nameref}"
array[1]
$ nameref=eleven
$ declare -p array
declare -a array=([0]="four" [1]="eleven" [2]="twelve" [3]="seven")
# This works.
$ printf '%s\n' "${nameref[*]@A}"

# Expands to nothing.
$ printf '%s\n' "${nameref@A}"

# Expands to nothing.
$ nameref=( 100 101 102 )
bash: `array[1]': not a valid identifier
# This error message doesn't make a lot of sense.
$ array[1]=( 100 101 102 )
bash: array[1]: cannot assign list to array member
# This error message would've been better for that.

The nameref can refer to the entire array *or* a single element of the
array, and most operations work how you would expect them to, either
way.

> Similarly for ${var@a}:
>
> $ echo ${nameref@a}
> a

$ declare -n nameref=array
$ printf '%s\n' "${!nameref@a}"
a
$ printf '%s\n' "${!nameref[1]@a}"

$ printf '%s\n' "${nameref[1]@a}"
a
$ printf '%s\n' "${nameref[*]@a}"
a a a a

> I was wondering if you could change the behavior of @a and @A expansions on 
> namerefs to match the output
> of declare -p ?

On Fri, Oct 17, 2025 at 3:48 PM Chet Ramey <[email protected]> wrote:
>
> Interesting question. In every other word expansion, referencing a nameref
> variable performs the resolution and uses the variable the nameref resolves
> to. The only places you can get to the nameref directly are `declare' and
> `unset'. Is this enough of a special case to make it different?

$ printf '%s\n' "${!nameref@Q}"
'four'
$ printf '%s\n' "${!nameref[1]@Q}"

# Nothing.
$ printf '%s\n' "${!nameref[0]@Q}"

# Nothing

${!parameter[index]@operator} could be made to dereference the nameref
like it appears to do with parameter transformations lacking an index.
If the @A and @a parameter transformations are made to mirror declare
-p, today's functionality would still be available. Might be hard to
arrive at these expansions without some documentation specific to
them, though.

Reply via email to