Ist es möglich, die Differenz von zwei Arrays in Bash zu nehmen.
Wäre wirklich großartig, wenn Sie mir den Weg empfehlen könnten.
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" )
Schätze deine Hilfe.
Wenn Sie unbedingt Array1 - Array2
wollen, dann
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
Die Laufzeit kann mit assoziativen Arrays verbessert werden, aber ich persönlich würde mich nicht darum kümmern. Wenn Sie so viele Daten bearbeiten, dass dies wichtig ist, ist Shell das falsche Werkzeug.
Für einen symmetrischen Unterschied wie Dennis 's Antwort funktionieren vorhandene Werkzeuge wie comm
, solange wir die Eingabe und Ausgabe ein wenig massieren (da sie auf linienbasierten Dateien und nicht auf Shell-Variablen arbeiten).
Hier weisen wir die Shell an, das Array mit Zeilenumbrüchen zu einer einzigen Zeichenfolge zusammenzufügen und Registerkarten zu verwerfen, wenn Zeilen aus comm
in ein Array zurückgelesen werden.
$ oldIFS = $ IFS IFS = $ '\ n\t' $ Array3 = ($ (Komm -3 <(Echo "$ {Array1 [*]}")) <(Echo "$ {Array2 [ *]} "))) comm: Datei 1 ist nicht in sortierter Reihenfolge $ IFS = $ oldIFS $ declare -p Array3 declare -a Array3 = '([0] =" key7 "[1] =" key8 "[2] =" key9 "[3] =" key10 ") '
Es beschwert sich, weil key1 < … < key9 > key10
durch lexographische Sortierung. Da beide Eingabearrays jedoch ähnlich sortiert sind, können Sie diese Warnung ignorieren. Sie können --nocheck-order
verwenden, um die Warnung zu entfernen, oder einen | sort -u
innerhalb der <(…)
-Prozessersetzung hinzufügen, wenn Sie die Reihenfolge und Eindeutigkeit der Eingabearrays nicht garantieren können.
echo ${Array1[@]} ${Array2[@]} | tr ' ' '\n' | sort | uniq -u
Ausgabe
key10
key7
key8
key9
Sie können bei Bedarf eine Sortierung hinzufügen
Jedes Mal, wenn eine Frage auftaucht, die sich auf eindeutige Werte bezieht, die möglicherweise nicht sortiert werden, geht mein Verstand sofort in die falsche Richtung. Hier ist meine Meinung dazu.
#!/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
* Hinweis **: Wie bei anderen Antworten werden doppelte Schlüssel in einem Array nur einmal gemeldet. Dies kann das Verhalten sein, nach dem Sie suchen. Der Awk-Code, der damit umgehen soll, ist unordentlich und nicht so sauber.
Verwenden Sie ARR1
und ARR2
als Argumente, verwenden Sie comm
, um den Job auszuführen, und mapfile
, um ihn in das RESULT
-Array zurückzusetzen:
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"
Beachten Sie, dass das Ergebnis die Quellreihenfolge möglicherweise nicht erfüllt.
Bonus aka "dafür sind Sie hier":
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"
Die Verwendung dieser kniffligen Evals ist die am wenigsten schlimmste Option, unter anderem bei Array-Parametern, die in bash übergeben werden.
Schauen Sie sich auch die comm
-Manpage an. Basierend auf diesem Code ist es sehr einfach zu implementieren, zum Beispiel array_intersect
: Verwenden Sie einfach -12 als Kommunikationsoptionen.
In 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
Bearbeiten:
ephemient weist auf einen möglicherweise schwerwiegenden Fehler hin. Wenn ein Element in einem Array mit einem oder mehreren Duplikaten vorhanden ist und im anderen Array überhaupt nicht vorhanden ist, wird es falsch aus der Liste der eindeutigen Werte entfernt. Die nachfolgende Version versucht, mit dieser Situation umzugehen.
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[@]}"
ausgabe
$ ./Shell.sh
Array4: key7 key8 key9 key10
Array4: key11
Es ist auch möglich, Regex zu verwenden (basierend auf einer anderen Antwort: Array-Schnittpunkt 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[@]}:
Ergebnis:
$ bash diff-arrays.sh
4 7 10 12