Turning a variable into an array
- #variables
- #declare
- #arrays
- #bugfix
declare -a NAME is usually read as "make me an array". But if NAME
already exists, it is really a request to change a variable's shape, and
bash answers that request from a small table: sometimes it carries the old
value over, sometimes it refuses outright, and in one case it quietly empties
the variable. huck had three partial copies of that table — one in declare,
one in local, one for the readonly check — and they disagreed with bash in
different directions.
The worst cell lost data:
# before
$ huck -c 'declare -A conf=([host]=localhost); declare -a conf; echo rc=$?; declare -p conf'
rc=0
declare -a conf=([0]="")# after
$ huck -c 'declare -A conf=([host]=localhost); declare -a conf; echo rc=$?; declare -p conf'
huck: line 1: declare: conf: cannot convert associative to indexed array
rc=1
declare -A conf=([host]="localhost" )An associative array and an indexed array are not interchangeable, so bash
refuses to convert between them in either direction and leaves the variable
alone. huck used to accept the -a and rebuild conf as an indexed array
with one empty element — the keys and values simply gone, exit status 0.
Going the other way, huck was too strict rather than too loose. A plain
string can become an array: bash files it under index 0.
# before
$ huck -c 'x=hello; declare -A x; declare -p x'
huck: line 1: declare: x: cannot convert scalar to associative array
declare -- x="hello"# after
$ huck -c 'x=hello; declare -A x; declare -p x'
declare -A x=([0]="hello" )readonly is about the value
readonly guards what a variable is, not what shape it is stored in.
Re-filing 1 as element 0 is the same data under a new shape, so bash
allows it — and huck was refusing, twice over:
# before
$ huck -c 'readonly R=1; declare -a R; declare -p R'
huck: line 1: R: readonly variable
huck: line 1: declare: R: readonly variable
declare -r R="1"# after
$ huck -c 'readonly R=1; declare -a R; declare -p R'
declare -ar R=([0]="1")A later R=7 is still refused. That check lives on assignment, which is
where it belongs.
The odd cell
One entry in the table looks like a bug in bash and is not. Inside a function, changing the shape of a name that is already local to that same function throws the value away, where the identical change at the top level promotes it:
# before
$ huck -c 'f(){ local v=s; local -a v; declare -p v; }; f'
declare -a v=([0]="s")# after
$ huck -c 'f(){ local v=s; local -a v; declare -p v; }; f'
declare -a v=()Re-declaring the same shape keeps the value, so this only fires on a real
change. It is now one function with the table written out beside it, which
declare, local, readonly and export all call.
Still open, all found by measuring the neighbouring cells: the same refusal
is missing when the declaration carries a value
(#347), a bare word list
assigned to an associative array is rejected instead of read as key/value
pairs (#783),
declare +a names the flag rather than the variable in its error
(#784), declare -aA is
rejected where bash applies both flags in order
(#785), and a shape flag
applied to a nameref lands on the nameref instead of its target
(#786).