是否有可能在bash中取两个数组的差异。 如果你可以建议我这样做的话会非常棒。
代码:
Array1=( "key1" "key2" "key3" "key4" "key5" "key6" "key7" "key8" "key9" "key10" )
Array2=( "key1" "key2" "key3" "key4" "key5" "key6" )
Array3 =diff(Array1, Array2)
Array3 ideally should be :
Array3=( "key7" "key8" "key9" "key10" )
感谢您的帮助。
如果你严格要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的答案这样的对称差异,像comm
这样的现有工具可以工作,只要我们按下输入和输出一点(因为它们在基于行的文件上工作,而不是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")'
它抱怨因为,通过词法排序,key1 < … < key9 > key10
。但由于两个输入数组的排序方式相似,因此可以忽略该警告。如果不能保证输入数组的顺序和唯一性,可以使用--nocheck-order
去除警告,或在| sort -u
进程替换中添加<(…)
。
echo ${Array1[@]} ${Array2[@]} | tr ' ' '\n' | sort | uniq -u
产量
key10
key7
key8
key9
如果需要,您可以添加排序
任何时候弹出一个问题来处理可能没有排序的唯一值,我的脑海里立刻就会变成awk。这是我的看法。
#!/bin/bash
diff(){
awk 'BEGIN{RS=ORS=" "}
{NR==FNR?a[$0]++:a[$0]--}
END{for(k in a)if(a[k])print k}' <(echo -n "${!1}") <(echo -n "${!2}")
}
Array1=( "key1" "key2" "key3" "key4" "key5" "key6" "key7" "key8" "key9" "key10" )
Array2=( "key1" "key2" "key3" "key4" "key5" "key6" )
Array3=($(diff Array1[@] Array2[@]))
echo ${Array3[@]}
$ ./diffArray.sh
key10 key7 key8 key9
*注意**:与给出的其他答案一样,如果数组中有重复的键,则只会报告一次;这可能是也可能不是您正在寻找的行为。要处理的awk代码更混乱,而不是干净。
将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"
请注意,结果可能不符合源订单。
奖金又名“这就是你在这里”:
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"
使用那些棘手的逃避是处理在bash中传递的数组参数的最差的选择。
另外,看看comm
手册页;基于此代码,它很容易实现,例如,array_intersect
:只需使用-12作为comm选项。
在Bash 4中:
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=( "key1" "key2" "key3" "key4" "key5" "key6" "key7" "key8" "key9" "key10" )
Array2=( "key1" "key2" "key3" "key4" "key5" "key6" )
Array3=( "key1" "key2" "key3" "key4" "key5" "key6" "key11" )
a1=${Array1[@]};a2=${Array2[@]}; a3=${Array3[@]}
diff(){
a1="$1"
a2="$2"
awk -va1="$a1" -va2="$a2" '
BEGIN{
m= split(a1, A1," ")
n= split(a2, t," ")
for(i=1;i<=n;i++) { A2[t[i]] }
for (i=1;i<=m;i++){
if( ! (A1[i] in A2) ){
printf A1[i]" "
}
}
}'
}
Array4=( $(diff "$a1" "$a2") ) #compare a1 against a2
echo "Array4: ${Array4[@]}"
Array4=( $(diff "$a3" "$a1") ) #compare a3 against a1
echo "Array4: ${Array4[@]}"
产量
$ ./shell.sh
Array4: key7 key8 key9 key10
Array4: key11
也可以使用正则表达式(基于另一个答案:Array intersection in bash):
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