Arrays Bash中两个数组的比较/差异

Arrays Bash中两个数组的比较/差异,arrays,bash,diff,compare,Arrays,Bash,Diff,Compare,是否可以在Bash中获取两个数组的差异。做这件事的好方法是什么 代码: 在bash4中: declare -A temp # associative array for element in "${Array1[@]}" "${Array2[@]}" do ((temp[$element]++)) done for element in "${!temp[@]}" do if (( ${temp[$element]} > 1 )) then u

是否可以在Bash中获取两个数组的差异。做这件事的好方法是什么

代码:

在bash4中:

declare -A temp    # associative array
for element in "${Array1[@]}" "${Array2[@]}"
do
    ((temp[$element]++))
done
for element in "${!temp[@]}"
do
    if (( ${temp[$element]} > 1 ))
    then
        unset "temp[$element]"
    fi
done
Array3=(${!temp[@]})    # retrieve the keys as values
编辑:

ephemient指出了一个潜在的严重缺陷。如果一个元素存在于一个具有一个或多个重复项的数组中,而在另一个数组中根本不存在,则该元素将从唯一值列表中错误删除。下面的版本试图处理这种情况

declare -A temp1 temp2    # associative arrays
for element in "${Array1[@]}"
do
    ((temp1[$element]++))
done

for element in "${Array2[@]}"
do
    ((temp2[$element]++))
done

for element in "${!temp1[@]}"
do
    if (( ${temp1[$element]} >= 1 && ${temp2[$element]-0} >= 1 ))
    then
        unset "temp1[$element]" "temp2[$element]"
    fi
done
Array3=(${!temp1[@]} ${!temp2[@]})

如果您严格要求
Array1-Array2
,则

Array1=( "key1" "key2" "key3" "key4" "key5" "key6" "key7" "key8" "key9" "key10" )
Array2=( "key1" "key2" "key3" "key4" "key5" "key6" )

Array3=()
for i in "${Array1[@]}"; do
    skip=
    for j in "${Array2[@]}"; do
        [[ $i == $j ]] && { skip=1; break; }
    done
    [[ -n $skip ]] || Array3+=("$i")
done
declare -p Array3
使用关联数组可能会改进运行时,但我个人不会这么做。如果您处理的数据足够重要,那么shell是错误的工具


对于Dennis的答案这样的对称差异,只要我们稍微调整一下输入和输出(因为它们在基于行的文件上工作,而不是在shell变量上工作),现有的工具就可以工作

在这里,我们告诉shell使用换行符将数组连接成单个字符串,并在从
comm
读回数组中的行时丢弃制表符

$ oldIFS=$IFS IFS=$'\n\t' $ Array3=($(comm -3 <(echo "${Array1[*]}") <(echo "${Array2[*]}"))) comm: file 1 is not in sorted order $ IFS=$oldIFS $ declare -p Array3 declare -a Array3='([0]="key7" [1]="key8" [2]="key9" [3]="key10")' $oldIFS=$IFS IFS=$'\n\t
$Array3=($(comm-3每当出现一个关于可能无法排序的唯一值的问题时,我的大脑就会立即转到awk。以下是我的看法

代码 *注意**:与给出的其他答案一样,如果数组中存在重复的键,则只会报告一次;这可能是您正在寻找的行为,也可能不是您正在寻找的行为。要处理的awk代码更混乱,也没有那么干净

echo ${Array1[@]} ${Array2[@]} | tr ' ' '\n' | sort | uniq -u
输出

key10
key7
key8
key9

如果需要,可以添加排序

也可以使用正则表达式(根据另一个答案:):

结果:

$ bash diff-arrays.sh 
4 7 10 12

ARR1
ARR2
作为参数,使用
comm
执行作业,并使用
mapfile
将其放回
RESULT
数组:

ARR1=("key1" "key2" "key3" "key4" "key5" "key6" "key7" "key8" "key9" "key10")
ARR2=("key1" "key2" "key3" "key4" "key5" "key6")

mapfile -t RESULT < \
    <(comm -23 \
        <(IFS=$'\n'; echo "${ARR1[*]}" | sort) \
        <(IFS=$'\n'; echo "${ARR2[*]}" | sort) \
    )

echo "${RESULT[@]}" # outputs "key10 key7 key8 key9"
ARR1=(“键1”“键2”“键3”“键4”“键5”“键6”“键7”“键8”“键9”“键10”)
ARR2=(“键1”“键2”“键3”“键4”“键5”“键6”)
映射文件-t结果<\

这执行对称差异,并假设原始数组没有重复。因此,这不是我首先想到的,但对于OP的一个示例来说效果很好。@ephemient:对,并行将是to
diff(1)
这也是对称的。此外,此脚本只需将元素添加到第一个版本第二行的列表中,即可查找任意数量数组的唯一元素。我添加了一个编辑,该编辑提供了一个版本来处理一个数组中未出现的重复项。非常感谢。我在想是否存在任何明显的重复项方法..如果我不知道有任何命令可以提供两个数组的差异..感谢您的支持和帮助。我修改了代码以读取两个文件的差异,这更容易编程您的第二个代码片段将无法工作,因为
仅在
(…)
中工作,而在
[…]中不工作
;在后者中,它必须是
-gt
;但是,由于您可能是指
=
而不是
应该替换为
-ge
。以明确说明什么是“对称的”在此上下文中的意思是:输出是一个数组,其中包含两个数组中唯一的值。@mklement0:
在双方括号内工作,但在词汇上而不是数字上。因此,在比较整数时,应使用双括号-因此您在这方面是正确的。我更新了我的答案acc第1个代码段为+1,它也适用于带有嵌入空格的元素。第2个代码段仅适用于带有嵌入空格的元素。如果您只需将
IFS=$'\n\t'
直接前置到
Array3=…
命令,则可以不保存和恢复
$IFS
。@mklement0您建议的命令:
IFS=$'\n\t'数组3=(…)
将设置
全局IFS
。试试看!@gniourf\u gniourf:谢谢你抓住了这一点!因为我的谬误可能对其他人也有诱惑力,所以我将留下我的原始评论并在这里解释:虽然将一个特殊的命令局部变量赋值预先添加到一个简单的命令是一种常见且有用的习惯用法,但它在这里不起作用,因为我的命令d完全由赋值组成。赋值后没有命令名(外部可执行文件,内置),这使得所有赋值都是全局的(在当前shell的上下文中);请参见
manbash
,章节
SIMPLE command EXPANSION
)。您能给出一个如何在C-shell(csh)中实现这一点的示例吗“@Stefan:Ugh,csh不应该被使用。
set Array3=()
foreach i($Array1)
set skip=0
foreach j($Array2)
如果($i=“$j”)那么
设置skip=1
中断
endif
结束
如果($skip==0),那么
设置Array3=($Array3:q“$i”)
endif
end
所有控制语句都需要在各自的行上。为了总结行为和约束条件:(a)执行对称差异:输出一个数组,其中的元素对于任一输入数组都是唯一的(OP的样本数据恰好与第一个数组唯一的输出元素相同),(b)仅与没有嵌入空格的元素(满足OP的要求)和(c)一起工作输出数组中元素的顺序与输入元素的顺序没有保证的关系,这是因为
awk
无条件地使用了关联数组——正如示例输出所证明的那样。此外,这个答案使用了一个巧妙的、值得注意但却令人费解的解决方法,来解决bash不支持将数组作为参数传递的问题nts:
Array1[@]
Array2[@]
作为字符串传递给shell函数
diff()
(作为参数
$1
$2
,与往常一样)。shell函数然后使用bash的
$ ./diffArray.sh
key10 key7 key8 key9
echo ${Array1[@]} ${Array2[@]} | tr ' ' '\n' | sort | uniq -u
key10
key7
key8
key9
list1=( 1 2 3 4   6 7 8 9 10 11 12)
list2=( 1 2 3   5 6   8 9    11 )

l2=" ${list2[*]} "                    # add framing blanks
for item in ${list1[@]}; do
  if ! [[ $l2 =~ " $item " ]] ; then    # use $item as regexp
    result+=($item)
  fi
done
echo  ${result[@]}:
$ bash diff-arrays.sh 
4 7 10 12
ARR1=("key1" "key2" "key3" "key4" "key5" "key6" "key7" "key8" "key9" "key10")
ARR2=("key1" "key2" "key3" "key4" "key5" "key6")

mapfile -t RESULT < \
    <(comm -23 \
        <(IFS=$'\n'; echo "${ARR1[*]}" | sort) \
        <(IFS=$'\n'; echo "${ARR2[*]}" | sort) \
    )

echo "${RESULT[@]}" # outputs "key10 key7 key8 key9"
function array_diff {
    eval local ARR1=\(\"\${$2[@]}\"\)
    eval local ARR2=\(\"\${$3[@]}\"\)
    local IFS=$'\n'
    mapfile -t $1 < <(comm -23 <(echo "${ARR1[*]}" | sort) <(echo "${ARR2[*]}" | sort))
}

# usage:
array_diff RESULT ARR1 ARR2
echo "${RESULT[@]}" # outputs "key10 key7 key8 key9"