Est-il possible de prendre la différence de deux tableaux en bash.
Ce serait vraiment bien si vous pouviez me proposer le moyen de le faire.
Code:
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" )
Apprécier ton aide.
Si vous voulez strictement Array1 - Array2
, alors
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
Le runtime peut être amélioré avec des tableaux associatifs, mais personnellement, je ne m'en soucierais pas. Si vous manipulez suffisamment de données pour que cela compte, Shell est le mauvais outil.
Pour une différence symétrique comme celle de Dennis, les outils existants tels que comm
work fonctionnent aussi longtemps que nous modifions un peu les entrées et les sorties (puisqu'elles fonctionnent sur des fichiers linéaires, pas sur des variables Shell).
Ici, nous invitons le shell à utiliser les nouvelles lignes pour joindre le tableau en une seule chaîne, et ignorons les tabulations lors de la lecture des lignes de comm
dans un tableau.
$ oldIFS = $ IFS IFS = $ '\ n\t' $ Array3 = ($ comm -3 <(echo "$ {Array1 [*]}") <(echo "$ {Array2 [ *]} "))) comm: le fichier 1 n'est pas dans l'ordre de tri $ IFS = $ oldIFS $ declare -p Array3 declare -a Array3 = '([0] =" key7 "[1] =" key8 "[2] =" key9 "[3] =" key10 ") '
Il se plaint parce que, par tri lexographique, key1 < … < key9 > key10
. Mais comme les deux tableaux d’entrée sont triés de la même manière, il est bon d’ignorer cet avertissement. Vous pouvez utiliser --nocheck-order
pour supprimer l'avertissement ou ajouter un | sort -u
dans le processus de substitution <(…)
si vous ne pouvez pas garantir l'ordre et l'unicité des tableaux d'entrée.
echo ${Array1[@]} ${Array2[@]} | tr ' ' '\n' | sort | uniq -u
Sortie
key10
key7
key8
key9
Vous pouvez ajouter un tri si vous avez besoin
Chaque fois qu'une question surgit concernant des valeurs uniques qui ne peuvent pas être triées, mon esprit devient immédiatement stupide. Voici mon point de vue sur elle.
#!/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
* Remarque **: Comme pour les autres réponses, s'il y a des clés en double dans un tableau, elles ne seront signalées qu'une fois. cela peut ou peut ne pas être le comportement que vous recherchez. Le code awk à gérer est plus compliqué et moins propre.
Avec ARR1
et ARR2
comme arguments, utilisez comm
pour exécuter le travail et mapfile
pour le remettre dans le tableau 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"
Notez que le résultat peut ne pas correspondre à l'ordre source.
Bonus aka "c'est ce que vous êtes ici pour":
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"
L'utilisation de ces évaluations difficiles est la moins mauvaise des options parmi d'autres traitant des paramètres de tableau transmis en bash.
Consultez également la page de manuel comm
; basé sur ce code, il est très facile à implémenter, par exemple, array_intersect
: utilisez simplement -12 comme options de communication.
Dans 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
Modifier:
ephemient a signalé un bug potentiellement sérieux. Si un élément existe dans un tableau avec un ou plusieurs doublons et n'existe pas du tout dans l'autre tableau, il sera incorrectement supprimé de la liste des valeurs uniques. La version ci-dessous tente de gérer cette situation.
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[@]}"
sortie
$ ./Shell.sh
Array4: key7 key8 key9 key10
Array4: key11
Il est également possible d'utiliser regex (basé sur une autre réponse: Intersection de tableau dans 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[@]}:
Résultat:
$ bash diff-arrays.sh
4 7 10 12