3

From https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html

The basic form of parameter expansion is ${parameter}. ...

If the first character of parameter is an exclamation point (!), a level of variable indirection is introduced. Bash uses the value of the variable formed from the rest of parameter as the name of the variable; this variable is then expanded and that value is used in the rest of the substitution, rather than the value of parameter itself. This is known as indirect expansion. The exceptions to this are the expansions of ${!prefix} and ${!name[@]} described below. The exclamation point must immediately follow the left brace in order to introduce indirection.

...

${!prefix*} ${!prefix@} Expands to the names of variables whose names begin with prefix , separated by the fi rst character of the IFS special variable. When ‘@’ is used and the expan- sion appears within double quotes, each variable name expands to a separate word.

${!name[@]} ${!name[*]} If name is an array variable, expands to the list of array indices (keys) assigned in name. If name is not an array, expands to 0 if name is set and null otherwise. When ‘@’ is used and the expansion appears within double quotes, each key expands to a separate word.

Can you give some examples for the quoted paragraphs? I have no clue what they mean.

2 Answers 2

10

We need to compare (and distinguish between):

"${Var}"          # Plain variable
"${!Var}"         # Indirect expansion
"${!Var@}"        # Prefix expansion
"${!Var[@]}"      # Array keys expansion
"${Var[@]}"       # Plain array expansion

There are also the * expansions which are very similar but have a small difference.

Indirection

Example for indirection:

$ varname=var_one
$ var_one=a-value

$ echo "${varname}"
var_one
$ echo "${!varname} and ${var_one}"
a-value and a-value

Prefix

Example for prefix:

$ head_one=foo
$ head_two=bar

$ printf '<%s> ' "${!head@}"
<head_one> <head_two>
$ printf '<%s> ' "${!head*}"
<head_one head_two>

Note that the variables are glued together by the first character of IFS, which by default is an space (as IFS is Space Tab NewLine by default).


Plain Array

Example of Array (no ! used) to show the small (but important) difference of @ and *:

$ Array[1]=This
$ Array[2]=is
$ Array[3]=a
$ Array[4]=simple
$ Array[5]=test.

$ printf '<%s> ' "${Array[@]}"
<This> <is> <a> <simple> <test.>

$ printf '<%s> ' "${Array[*]}"
<This is a simple test.>

The same comment about IFS apply here.

Note that I did not assign the index 0 (on purpose) of Array.

Note that a simpler way to assign the Array is:

$ Array=( "" This is a simple test.)

But here the index 0 must be used, and I used an empty value (which is not the same as an un-set value as above).


Array LIST

For this, a simple indexed array (with numbers) is not so fun:

$ Array=( "" A simple example of an array.)
$ printf '<%s> ' "${!Array[@]}"
<0> <1> <2> <3> <4> <5> <6> 
$ printf '<%s> ' "${!Array[*]}"
<0 1 2 3 4 5 6>

But for a Associative array, things become more interesting

$ unset Array                               # erase any notion of variable array.
$ declare -A Array                          # make it associative

$ Array=([foo]=one [bar]=two [baz]=three)   # give it values.

$ printf '<%s> ' "${Array[@]}"
<two> <three> <one>                         # List of values.

$ printf '<%s> ' "${!Array[@]}"
<bar> <baz> <foo>                           # List of keys

$ printf '<%s> ' "${Array[*]}"
<two three one>                             # One string of list of values.

$ printf '<%s> ' "${!Array[*]}"
<bar baz foo>                               # One string of list of keys.

Please note that the order is not the same as when assigned.


Note: All the uses I presented are quoted "${!Array[@]}", both the unquoted values ${!Array[@]} and ${!Array[*]} work exactly equal, give the same output (in Bash).
But are affected by shell splitting on the IFS value. And the ugly, always problematic "Pathname expansion". Not so useful in general. Or to be used very carefully, in any case.

0

Take a look at BinaryZebra's answer for a detailed explanation. The below quote from TLDP is said to be incorrect.

Original:

Looking at http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html:

If the first character of "PARAMETER" is an exclamation point, Bash uses the value of the variable formed from the rest of "PARAMETER" as the name of the variable; this variable is then expanded and that value is used in the rest of the substitution, rather than the value of "PARAMETER" itself. This is known as indirect expansion.

You are certainly familiar with straight parameter expansion, since it happens all the time, even in the simplest of cases, such as the one above or the following:

franky ~> echo $SHELL
/bin/bash

The following is an example of indirect expansion:

franky ~> echo ${!N*}
NNTPPORT NNTPSERVER NPX_PLUGIN_PATH

Note that this is not the same as echo $N*.

You can see the effects of this in your shell.

Example:

$ TEST=(test1 test2 test3)
$ echo ${!TEST*}
TEST
$ echo ${!TEST[@]}
0 1 2
$ echo ${TEST[@]}
test1 test2 test3
$ echo ${#TEST[@]}
4

Note the difference between using ${!TEST[@]}, ${TEST[@]}, and ${#TEST[@]}.

4
  • 1
    @BinaryZebra +1. Those TLDP bash guides are often problematic with mistakes and ugly code examples (l'd liek to just say lame.) For the ugly code part, I once saw someone kept using stuff like foo; exitval=$?; if [ "$exitval" != 0 ] instead of if foo, and after asking him he said that comes from TLDP ABS. Commented Dec 5, 2015 at 21:13
  • The expansion of ${!N*} is not Indirect, it is a "Prefix expansion". The examples given have only Array [@] forms. No mention of the others.
    – user79743
    Commented Dec 5, 2015 at 21:22
  • @BinaryZebra Thanks for the tip that the TLDP docs are wrong.
    – Sly
    Commented Dec 5, 2015 at 21:24
  • 1
    @Arthur2e5 Thanks, and yes, the TLDP may be problematic.
    – user79743
    Commented Dec 5, 2015 at 21:31

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .