如何在Bash中复制一个数组?

114

我有一个应用程序数组,像这样初始化:

depends=$(cat ~/Depends.txt)

当我尝试解析列表并使用以下方法将其复制到新数组中时,
for i in "${depends[@]}"; do
   if [ $i #isn't installed ]; then
      newDepends+=("$i")
   fi
done

发生的情况是,只有 depends 的第一个元素最终出现在 newDepends 中。
for i in "${newDepends[@]}"; do
   echo $i
done

^^ 这将只输出一个东西。因此,我正在努力弄清楚为什么我的for循环只移动第一个元素。整个列表最初都在depends上,所以不是那个问题,但我已经没有更多的想法了。


我觉得那看起来很好。你确定你的“未安装”测试正常工作了吗? - rici
4
你的问题中是否有错别字?"depends"将由两个单词组成,即"cat"和"/Depends",而不是"/Depends.txt"文件的内容。 - chepner
11个回答

194
a=(foo bar "foo 1" "bar two")  #create an array
b=("${a[@]}")                  #copy the array in another one 

for value in "${b[@]}" ; do    #print the new array 
echo "$value" 
done   

4
只写 b=(${a[@]}) 可以吗? - Indicator
16
尝试使用您的修改执行该命令,您将了解为什么不正确地书写而不引用值。值 "foo 1" 不会像应该显示,但它会为数组 foo AND 1,bar AND two 创建更多的值,而不是 "foo 1" 和 "bar two"。 - user1088530
3
@指示器 你会遇到空格的问题! - Eduardo Lucio
6
同样适用于复制命令行参数,例如 b=("$@") - Huw Walters
2
如果某些元素为空,则此操作会更改元素的顺序。 - Shōgun8

57

在Bash中复制非关联数组的最简单方法是:

arrayClone=("${oldArray[@]}")

或者将元素添加到已存在的数组中:

someArray+=("${oldArray[@]}")

元素中的换行符/空格/IFS将被保留。

对于复制关联数组,Isaac的解决方案非常有效。


1
这对我似乎没有起作用:$ a1=( big harry man ); $ a2=( "${!a1[@]}" ); $ echo "${a2[0]}"; 0 $ echo "${a2[1]}"; 1; $ - Felipe Alvarez
3
嗨@FelipeAlvarez - 的确,!是多余的,我已经更正了我的答案。 - niieani
1
如果一些元素为空,这会改变元素的顺序。 - Shōgun8

9

其他答案中提供的解决方案对于关联数组或具有非连续索引的数组无效。以下是更通用的解决方案:

declare -A arr=([this]=hello [\'that\']=world [theother]='and "goodbye"!')
temp=$(declare -p arr)
eval "${temp/arr=/newarr=}"

diff <(echo "$temp") <(declare -p newarr | sed 's/newarr=/arr=/')
# no output

And another:

declare -A arr=([this]=hello [\'that\']=world [theother]='and "goodbye"!')
declare -A newarr
for idx in "${!arr[@]}"; do
    newarr[$idx]=${arr[$idx]}
done

diff <(echo "$temp") <(declare -p newarr | sed 's/newarr=/arr=/')
# no output

2
你的第一个解决方案不能成为一个函数,因为 eval "declare -A newarr=…" 会有效地声明一个 本地 数组 newarr,遮蔽了父环境中真正的变量。也许可以删除 declare -A,只留下 newarr=…,但这条路上存在转义问题。 - Anton Samsonov
你能解释一下你在这个一行代码中做了什么吗?例如,括号中的管道是什么意思?谢谢。 - von spotz
这个方法非常适用于保持元素的顺序。 - Shōgun8

6
尝试这个:arrayClone=("${oldArray[@]}") 这很容易实现。

1
但对于稀疏数组,它将压缩稀疏数组。 - anthony

3
array_copy() {
    set -- "$(declare -p $1)" "$2"
    eval "$2=${1#*=}"
}

# Usage examples:

these=(apple banana catalog dormant eagle fruit goose hat icicle)
array_copy these those
declare -p those

declare -A src dest
source=(["It's a 15\" spike"]="and it's 1\" thick" [foo]=bar [baz]=qux)
array_copy src dest
declare -p dest

注意:在复制关联数组时,目标数组必须已经存在为关联数组。否则,array_copy()会将其创建为标准数组,并尝试将关联源的键名解释为算术变量名,结果很丑陋。
Isaac Schwabacher的解决方案在这方面更加健壮,但它无法整洁地包装在一个函数中,因为它的eval步骤会评估整个declare语句,而当它们在函数内部时,bash会将它们视为与local等效。可以通过将-g选项嵌入评估的declare中来解决这个问题,但这可能会使目标数组具有比它应该具有的更多作用域。我认为更好的方法是让array_copy()仅将实际副本复制到显式作用域的目标中。

3
您可以通过指定索引将第一个数组的元素插入到副本中来复制数组:
#!/bin/bash

array=( One Two Three Go! );
array_copy( );

let j=0;
for (( i=0; i<${#array[@]}; i++)
do
    if [[ $i -ne 1 ]]; then # change the test here to your 'isn't installed' test
        array_copy[$j]="${array[$i]}
        let i+=1;
    fi
done

for k in "${array_copy[@]}"; do
    echo $k
done

这将会产生以下输出:
One
Three
Go!

关于bash数组的有用文档可以在TLDP上找到。


1
TLDP网站很棒,但是bash已经有了显著的进步,而且它所推荐的许多内容现在被认为是不好的风格。除非你想要兼容bash-2.xdash/busybox,否则你可能需要找到一个更近期的参考资料。希望他们能在某个时候更新。 - Dave

2

问题是在函数中复制数组以便在父级代码中可见。这个解决方案适用于索引数组,并且如果在复制之前被预定义为declare -A ARRAY,也适用于关联数组。

最初的回答:在函数内部使用 declare -p ARRAY 命令将数组转换为字符串,并将其返回给父级代码。 然后,在父级代码中,使用 eval 命令将返回的字符串重新转换为数组。
function array_copy
# $1 original array name
# $2 new array name with the same content
{
    local INDEX
    eval "
        for INDEX in \"\${!$1[@]}\"
        do
            $2[\"\$INDEX\"]=\"\${$1[\$INDEX]}\"
        done
    "
}

0

Bash 4.3 开始,你可以这样做

$ alpha=(bravo charlie 'delta  3' '' foxtrot)

$ declare -n golf=alpha

$ echo "${golf[2]}"
delta  3

20
答案不正确。虽然 declare -n 命令很有用,但它只是创建对原始数组的引用,而不是复制它,这与问题所询问的不同。 - niieani

0

成功将一个数组复制到另一个数组中。

firstArray=()
secondArray=()

firstArray+=("Element1")
firstArray+=("Element2")

secondArray+=("${firstArray[@]}")

for element in "${secondArray[@]}"; do
  echo "${element}"
done

-1

我发现这对我有用(大部分时间 :)) ...

eval $(declare -p base | sed "s,base,target,")

将sed命令扩展到根据需要编辑任何开关,例如如果新结构必须是可写的,则编辑掉只读 (-r)。


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