如何在 Bash 中连接数组?

61
如何从标准输入读取行到一个数组中,然后将该数组与自身连接起来?
我的代码是:
countries=()
while read -r country; do
    countries+=( "$country" )
done
countries=countries+countries+countries # this is the wrong way, i want to know the right way to do it
echo "${countries[@]}"

请注意,我可以像下面的代码一样打印三次,但这不是我的目标。我必须将它们连接在数组中。
countries=()
while read -r country; do
    countries+=( "$country" )
done
echo "${countries[@]} ${countries[@]} ${countries[@]}"

你在自己的代码中添加了数组... - Jason Hu
@HuStmpHrrr,通过附加单值数组将单个项附加到数组中是可以的,但我可以理解有些人可能不明白他们已经在使用的语法的含义。 - Charles Duffy
但是我不知道如何添加它本身。谢谢。@HuStmpHrrr - Enamul Hassan
1
@Mehdi Charife,我不确定我会将示例输入和实际/预期输出描述为“不必要的内容”。在[mre]定义中,它们都是我们明确要求包含的内容。 - Charles Duffy
是的,让原帖作者的目标变得清晰明了正是关键所在;在这种情况下,最小可重现示例只是证明英文描述中没有涵盖到任何令人惊讶或异常的内容,但即便如此,它仍然很有用:通常我们会遇到那些在散文描述中漏掉某些东西的人。 - Charles Duffy
3个回答

85
首先,将您的列表读入数组中,每行一个条目:
readarray -t countries

如果使用较旧版本的bash,则可以使用以下命令:

# same, but compatible with bash 3.x; || is to avoid non-zero exit status.
IFS=$'\n' read -r -d '' countries || (( ${#countries[@]} ))

其次,要复制这些条目,可以将数组扩展到自身三倍:
countries=( "${countries[@]}" "${countries[@]}" "${countries[@]}" )

...或者使用现代语法来执行追加操作:

countries+=( "${countries[@]}" "${countries[@]}" )

在像最后一个例子中连接数组时,为什么它不使用"${countries[*]}"(而不是像打印时使用"${countries[@]}")呢? - jww
2
@jww,使用"${countries[*]}"将只添加一个数组元素,其中包含连接在一起的整个国家列表字符串,而不是每个国家一个元素。 - Charles Duffy
1
@jww,相比之下,不加引号的${countries[*]}会将国家名称中带有空格的国家(例如新几内亚)分隔成单独的列表元素(为每个“新”和“几内亚”添加一个单独的数组元素)。 - Charles Duffy

19

只需简单地写出这个:

countries=$(cat)
countries+=( "${countries[@]}" "${countries[@]}" )
echo ${countries[@]}

第一行是用于获取输入数组,第二行是用于连接数组,最后一行是用于打印数组。


2
countries=$(cat) 将从标准输入中获取的单个字符串分配给数组的第一个元素;各个元素不会被拆分为单独的国家。(使用 declare -p countries 显示数组的定义,行为将是明显的)。 - Charles Duffy
3
换句话说,当你这样做时,你的数组不是countries=( A B C A B C A B C ),而是countries=( "A B C" "A B C" "A B C" );这个问题并不明显,因为使用的echo命令引号不足,所以会分割其参数。使用printf '%q\n' "${countries[@]}",每个集合将在不同的行上,这也将使问题变得明显(如果数组被正确填充,每个国家将在不同的行上)。 - Charles Duffy

1
在Ubuntu 14.04上,以下代码将连接三个元素(元素计数为:3),每个元素都是一个名为countries的数组。
countries=( "${countries[@]}" "${countries[@]}" "${countries[@]}" )

而以下代码将所有元素连接成一个单一的数组:

countries=( ${countries[*]} ${countries[*]} ${countries[*]} )

这的数量将是30(考虑了原始帖子中指定的国家)。

2
这是错误的。countries=( "${countries[@]}" "${countries[@]}" "${countries[@]}" ) 会将原始数组扩展三倍,每次都为原始数组元素创建一个元素。相比之下,countries=( ${countries[*]} ${countries[*]} ${countries[*]} ) 则对原始数组进行了字符串分割和通配符扩展。假设你有 countries=( "新几内亚" "新西兰" "朝鲜" )result=( "${countries[@]}" "${countries[@]}" "${countries[@]}" ) 扩展为九个项。 - Charles Duffy
2
相比之下,result=( ${countries[*]} ${countries[*]} ${countries[*]} ) 扩展为十八个项目,因为每个 New 都成为自己的数组元素。(如果它分裂成的任何单词是可以匹配本地磁盘上的文件的通配符,那么情况会更加复杂!) - Charles Duffy
您可以在 https://ideone.com/Bj9LLM 上的在线解释器中运行此代码,并查看其输出。 - Charles Duffy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接