能解释一下吗?使用 array=$( command ) 和 array=( $( command ) ) 之间的数组行为有何区别?

能解释一下吗?使用 array=$( command ) 和 array=( $( command ) ) 之间的数组行为有何区别?

我理解命令替换。我了解子壳。我不明白为什么使用子 shell 会改变我的数组的结构。

给出此命令输出:(openstack 命令的使用并不相关)

bash$ floating ip list -c 'Floating IP Address' -f value
172.25.250.106
172.25.250.107
172.25.250.101

尝试在数组中捕获,但所有地址最终都在元素 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

整个输出被捕获为字符串,并且不被解析为元素。我期待每个词都成为一个元素。当我重复此操作以确保引用每个 IP 地址时(使用 -f csv 而不是 -f value),结果是相同的。

接下来,我将命令替换放入子 shell 中:

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

这是我最初期望的行为。我还注意到使用读取语句构建数组按预期工作:

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

我想看看原来的命令替换工作。想知道我是否应该先设置一个字段分隔符或者我还缺少什么。我试图了解导致行为差异的原因。

答案1

float=$( openstack floating ip list -c 'Floating IP Address' -f value )

这创建了一个细绳变量,而不是数组变量。该字符串是命令的输出,减去任何尾随换行符。

如果您尝试将字符串变量用作数组,它将被视为单元素数组,字符串值位于位置 0。

float=( $( openstack floating ip list -c 'Floating IP Address' -f value ) )

这不会“将命令替换放入子 shell 中”。命令替换本身$(…)会创建一个子 shell。它周围的括号不会创建另一个子shell:它们创建一个数组。数组内容是通过获取命令的输出、删除尾随换行符、拆分为空格分隔的单词列表以及将此列表中包含与一个或多个文件匹配的通配符的任何元素替换为匹配文件名的列表。

当括号位于需要命令的位置时,它们会创建一个子 shell。在 中var=(…),等号后面紧跟着的不是命令,而是赋值的值。在这种情况下,括号表示该值是一个数组。

答案2

float=$( openstack floating ip list -c 'Floating IP Address' -f value )

这是命令替换里面一个常规变量赋值,不是数组赋值。所有复合数组赋值都具有以下形式x=( ... ),正如您在下面使用的那样。这里没有子 shell(除了简单地作为替换命令的执行上下文之外)。

当您使用命令替换时,它的行为方式与具有这些内容的变量在该位置的行为相同,因此与float=$x此处类似,但也与ls $xor类似foo=($x)分词对扩展的结果执行,它将值分成变量中任何字符处的单独参数IFS

您可以通过引用扩展来抑制分词"$(...)"

如果你想创建一个分割词数组,你需要两个都创建一个数组并获取单词 split: 这意味着数组赋值foo=(...)和参数或命令替换$...相结合,如第二种情况:

float=( $( openstack floating ip list -c 'Floating IP Address' -f value ) )

创建float=(...)一个数组,元素来自命令替换期间执行的分词$(...)


可能令人困惑的是,当您将非数组用作一个数组时,Bash 会自动将它们转换为单例数组,因此看起来您确实获得了一个数组,但是包含单个项目。

echo ${float[0]}
echo ${#float[@]}

这是非常巧妙地记录下来

任何使用有效下标对变量的引用都是合法的,并且 bash 将在必要时创建一个数组。

如果您使用另一个索引,您会更清楚地看到这一点:

float[1]=abc
echo ${#float[@]} # => 2

转换过程仅使用变量的现有值(如果有)作为数组中索引 0 处的项。

答案3

中的括号var=( some things )不标记子外壳,它们是数组赋值语法。因此,如果没有它们,您将分配给常规(标量)变量。常规赋值的右侧不会进行分词,因此var=$(echo foo bar)会将字符串放入foo bar, 中var并添加空格。另一方面,数组赋值需要多个单词,因此 和 都arr=(foo bar)给出arr=( $(echo foo bar) )一个双元素数组。

相关内容