Home > database >  Why do the `man bash` pages state the `declare` and `local` `-n` attribute "cannot be applied t
Why do the `man bash` pages state the `declare` and `local` `-n` attribute "cannot be applied t

Time:02-11

Why does local -n work on array variables when the manual explicitly says it does not? Is the manual wrong? Is this relying on some sort of bash "undefined behavior?" Is the manual out-of-date? Am I missing something?

Here is the information I'm looking at from the bash manual:

Run man bash and search for local [ using the regular expression search pattern local \[. It says (emphasis added):

local [option] [name[=value] ... | - ]

For each argument, a local variable named name is created, and assigned value. The option can be any of the options accepted by declare.

(See also help local).

So, the options that can be passed to the bash local built-in command are the same as those of declare. Let's find what options can be passed to declare:

Run man bash and search for declare [ using the regular expression search pattern declare \[. Under the -n entry there for declare [-aAfFgilnrtux] [-p] [name[=value] ...] you'll see (emphasis added):

-n Give each name the nameref attribute, making it a name reference to another variable. That other variable is defined by the value of name. All references, assignments, and attribute modifications to name, except those using or changing the -n attribute itself, are performed on the variable referenced by name's value. The nameref attribute cannot be applied to array variables.

(See also help declare).

So, despite it saying "The nameref attribute cannot be applied to array variables,", it does work just fine on array variables!

To prove that, here is a demo of it working fine for regular bash arrays:

function foo {
    # declare a local **reference variable** (hence `-n`) named `data_ref`
    # which is a reference to the value stored in the first parameter
    # passed in
    local -n data_ref="$1"
    echo "${data_ref[0]}"
    echo "${data_ref[1]}"
}

# declare a regular bash "indexed" array
declare -a data
data =("Fred Flintstone")
data =("Barney Rubble")
foo "data"

Sample output:

Fred Flintstone
Barney Rubble

...and here is it working just fine on associative bash arrays (ie: bash hash tables, "dictionaries", or "unordered maps"):

function foo {
    # declare a local **reference variable** (hence `-n`) named `data_ref`
    # which is a reference to the value stored in the first parameter
    # passed in
    local -n data_ref="$1"
    echo "${data_ref["a"]}"
    echo "${data_ref["b"]}"
}

# declare a bash associative array
declare -A data
data["a"]="Fred Flintstone"
data["b"]="Barney Rubble"
foo "data"

Sample output:

Fred Flintstone
Barney Rubble

My bash --version is 4.4.20(1)-release, on Linux Ubuntu 18.04:

GNU bash, version 4.4.20(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3 : GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

References:

  1. I modified the above code samples from @Todd Lehman's answer here: How to pass an associative array as argument to a function in Bash?

Note: in case anyone is searching for it, this question itself also answers the questions: "How do I pass a bash array as a parameter to a function?" and "How do I pass a bash associative array as a parameter to a function?"

CodePudding user response:

You are applying -n to a scalar variable, and then have that nameref point to an array. This is fine.

What you can't do is apply -n to an array variable to create an array of namerefs:

# Declare two variables
declare foo=42 bar=1337

# Make an array of namerefs
declare -n array=(foo bar)    # ERROR

# ${array[0]} is foo so this should print $foo, i.e. 42?
echo "${array[0]}"    

If you try, Bash stops you:

bash: declare: array: reference variable cannot be an array
  • Related