I understand command substitution. I understand subshells. I do not understand why using a subshell changes the structure of my array.
Given this command output:
(use of openstack command is not intended to be relevant)
bash$ floating ip list -c 'Floating IP Address' -f value
172.25.250.106
172.25.250.107
172.25.250.101
Attempt to capture in an array, but all addresses end up in element 0:
bash$ float=$( openstack floating ip list -c 'Floating IP Address' -f value )
bash$ echo ${float[@]}
172.25.250.106 172.25.250.107 172.25.250.101
bash$ echo ${#float[@]}
1
bash$ echo ${float[0]}
172.25.250.106 172.25.250.107 172.25.250.101
bash$ echo ${#float[0]}
44
The whole output was captured as a string and not parsed into elements. I was expecting each word to become an element. When I repeat this to ensure that each IP address is quoted (using -f csv instead of -f value), the results are the same.
Next, I put the command substitution in a subshell:
bash$ unset float
bash$ float=( $( openstack floating ip list -c 'Floating IP Address' -f value ) )
bash$ echo ${float[@]}
172.25.250.106 172.25.250.107 172.25.250.101
bash$ echo ${#float[@]}
3
echo ${float[0]}
172.25.250.106
echo ${#float[0]}
14
This was the behavior I expected originally. I also notice that building the array using a read statement worked as expected:
bash$ unset float
bash$ read -a float <<< $( openstack floating ip list -c 'Floating IP Address' -f value )
bash$ echo ${float[@]}
172.25.250.106 172.25.250.107 172.25.250.101
bash$ echo ${#float[@]}
3
echo ${float[0]}
172.25.250.106
echo ${#float[0]}
14
I would like to see the original command substitution work. Wondering if I should have set a field separator first or what else I am missing. I am trying to understand what causes the difference in behavior.
Best Answer
This creates a string variable, not an array variable. The string is the output of the command, minus any trailing newline.
If you attempt to use a string variable as an array, it's treated as a single-element array with the string value at position 0.
This does not “put the command substitution in a subshell”. The command substitution itself
$(…)
creates a subshell. The parentheses around it do not create another subshell: they create an array. The array content it the list of words resulting from taking the output of the command, removing trailing newlines, splitting into a list of whitespace-separated words, and replacing any element of this list that contains wildcard characters that match one or more files by the list of matching file names.Parentheses create a subshell when they're at a location where a command is expected. In
var=(…)
, what is expected immediately after the equal sign is not a command, but a value for assignment. In this context, the parentheses indicate that the value is an array.