Bashで2つの配列の違いを取ることは可能ですか?.
あなたが私にそれを行う方法を提案できれば本当に素晴らしいでしょう。
コード:
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
_
連想配列を使用するとランタイムが改善される可能性がありますが、個人的には気にしません。そのために十分なデータを操作している場合、シェルは間違ったツールです。
デニスの答えのような対称的な違いについては、入力を少し処理し、シェル変数ではなく行ベースのファイルで動作するため、 comm
のような既存のツールが機能します)。
ここでは、改行を使用して配列を単一の文字列に結合し、comm
から配列に戻る行を読み取るときにタブを破棄するようシェルに指示します。
$ oldIFS = $ IFS IFS = $ '\ n\t' $ Array3 =($(comm -3 <(echo "$ {Array1 [*]}")<(echo " $ {Array2 [*]} "))) comm:ファイル1はソートされた順序ではありません $ 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コードは厄介で、それほどきれいではありません。
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潜在的に深刻なバグを指摘しました。要素が1つ以上の重複を持つ1つの配列に存在し、他の配列にまったく存在しない場合、一意の値のリストから誤って削除されます。以下のバージョンはその状況を処理しようとします。
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[@]})
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"
これらのトリッキーなevalsを使用することは、bashで渡す配列パラメーターを扱う他の中で最も最悪のオプションです。
また、comm
マンページをご覧ください。このコードに基づいて、非常に簡単に実装できます。たとえば、array_intersect
:通信オプションとして-12を使用します。
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
正規表現を使用することも可能です(別の答えに基づいて: 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