Configuration Information:
Machine: x86_64
OS: Linux 6.14.0-37-generic #37~24.04.1-Ubuntu
Compiler: gcc 13
uname output: Linux master04 6.14.0-37-generic #37~24.04.1-Ubuntu SMP 
PREEMPT_DYNAMIC Thu Nov 20 10:25:38 UTC 2 x86_64 x86_64 x86_64 GNU/Linux
Machine Type: x86_64-pc-linux-gnu
Bash Version: 5.3
Patch Level: 0
Release Status: release

Description:
 When using ${!nameref} where the nameref variable is declared with
 'declare -n', the indirect expansion correctly returns the name of
 the referenced variable (as documented in the manual).
 However, when adding a substring expansion suffix :offset:length,
 the behavior diverges depending on whether the referenced target
 is a scalar or an indexed array:
 - Scalar target: ${!nameref:0:n} correctly performs indirect
   expansion first (returning the referenced variable NAME), then
   applies the substring operation to that name.
 - Array target: ${!nameref:0:n} silently SKIPS the indirect
   expansion entirely and instead behaves as if the expression were
   ${array[0]:0:n} (i.e., nameref is transparently resolved, the
   first array element is returned, and substring is applied to it).
 This inconsistency is not documented anywhere in the Bash manual.
 The manual section on indirect expansion (Parameter Expansion) does
 not mention any distinction based on the type of the referenced
 variable.
Repeat-By:
 #!/usr/local/bin/bash
 arr=( "a" "b" "c" "d" )
 ab="cd"
 declare -n ref="arr"     # nameref -> array
 declare -n sref="ab"     # nameref -> scalar
 echo "=== Without :offset:length ==="
 echo "\${!ref}   = ${!ref}"       # prints: arr   (correct)
 echo "\${!sref}  = ${!sref}"      # prints: ab    (correct)
 echo ""
 echo "=== With :offset:length ==="
 echo "\${!ref:0:100}  = ${!ref:0:100}"    # prints: a    (UNEXPECTED)
 echo "\${!sref:0:4}   = ${!sref:0:4}"     # prints: ab   (correct)
 # Expected output for ${!ref:0:100}:
 #   "arr" (indirect expansion returns "arr", then :0:100 slices it)
 #
 # Actual output for ${!ref:0:100}:
 #   "a" (indirect expansion is skipped, nameref resolves to arr,
 #        ${arr:0:100} = ${arr[0]:0:100} = "a")
 echo ""
 echo "=== Additional evidence ==="
 # ${!ref} works correctly (returns "arr")
 # Adding :0:100 should NOT change the indirect expansion behavior
 # but it does -- only when the target is an array.
Fix:
 Either:
 (a) Make the behavior consistent: ${!nameref:o:l} should always
     perform indirect expansion first (return the referenced name),
     then apply :o:l -- regardless of target type. OR
 (b) Document the current divergent behavior in the manual, explaining
     that ${!nameref:o:l} skips indirect expansion when the nameref
     target is an indexed array.

Reply via email to