Configuration Information [Automatically generated, do not change]:
Machine: x86_64
OS: linux-gnu
Compiler: gcc
Compilation CFLAGS: -march=x86-64 -mtune=generic -O2 -pipe -fno-plt 
-fexceptions         -Wp,-D_FORTIFY_SOURCE=3 -Wformat -Werror=format-security   
      -fstack-clash-protection -fcf-protection         -fno-omit-frame-pointer 
-mno-omit-leaf-frame-pointer -g 
-ffile-prefix-map=/build/bash/src=/usr/src/debug/bash -flto=auto 
-DDEFAULT_PATH_VALUE='/usr/local/sbin:/usr/local/bin:/usr/bin' 
-DSTANDARD_UTILS_PATH='/usr/bin' -DSYS_BASHRC='/etc/bash.bashrc' 
-DSYS_BASH_LOGOUT='/etc/bash.bash_logout' -DNON_INTERACTIVE_LOGIN_SHELLS 
-std=gnu17
uname output: Linux desktop 6.18.29-1-lts #1 SMP PREEMPT_DYNAMIC Mon, 11 May 
2026 07:46:10 +0000 x86_64 GNU/Linux
Machine Type: x86_64-pc-linux-gnu

Bash Version: 5.3
Patch Level: 9
Release Status: release

Description:

When an array is declare'd within a function with some variation of "-ga" or
"-A" in quotes, the variable is created with inconsistent properties and may
have incorrect values. That is,

  f() { declare -ga arr=(1 2 3); }

works as expected, but with

  f() { declare "-ga" arr=(1 2 3); }

1) 'local -p arr' succeeds inside f() and the value is lost on return;
2) 'declare -p arr' outside f() shows a variable with no value, not even '=()';
3) the variable can not be found with "${!arr@}";

It gets even worse with associative arrays.

  $ f() { declare "-gA" arr=([a]=1 [b]=2); declare -p arr; }; f; declare -p arr

  declare -a arr=([0]="2")
  declare -A arr

  $ f() { local "-A" loc=([a]=1 [b]=2); local -p loc; }; f

  bash: local: arr: cannot convert indexed to associative array
  declare -a arr=([0]="2")

Both cases work properly when quotes are removed.

Non-arrays are not affected, eg. "-gi" works as expected in spite of the quotes.
This was discovered in an eval context, hence the paranoid overquoting.

Repeat-By:

  #!/bin/bash
  #set -u
  f() {
    declare "-g"   -- qg="text"
    declare  -g    -- ug="text"
    declare "-gi"  -- qgi="123"
    declare  -gi   -- ugi="123"
    declare "-ga"  -- qga=("elem1" "elem2")
    declare  -ga   -- uga=("elem1" "elem2")
    declare "-ga"  -- qga2=([0]="elem1" [1]="elem2")
    declare  -ga   -- uga2=([0]="elem1" [1]="elem2")
    declare "-gia" -- qgia=("3" "2" "1" "zero")
    declare  -gia  -- ugia=("3" "2" "1" "zero")
    declare "-gA"  -- qgA=([a]="elem1" [b]="elem2")
    declare  -gA   -- ugA=([a]="elem1" [b]="elem2")
    declare "-giA" -- qgiA=([a]="3" [b]="2" [c]="1" [d]="zero")
    declare  -giA  -- ugiA=([a]="3" [b]="2" [c]="1" [d]="zero")
    printf '\nlocals (should be none; "local: name: not found" suppressed):\n'
    local -p qg ug qgi ugi qga uga qga2 uga2 qgia ugia qgA ugA qgiA ugiA \
      2>/dev/null
  }
  f
  printf '\nglobals (declared with opts quoted then unquoted):\n'
  declare -p qg ug qgi ugi qga uga qga2 uga2 qgia ugia qgA ugA qgiA ugiA

Besides the surprising output from that script, when run with 'set -u' it also
reveals this:

  ./bug.sh: line 13: zero: unbound variable

So bash is also somehow trying to use an array value as a variable name.


Reply via email to