BASH에서 두 배열의 교차


12

다음과 같은 두 가지 배열이 있습니다.

A=(vol-175a3b54 vol-382c477b vol-8c027acf vol-93d6fed0 vol-71600106 vol-79f7970e vol-e3d6a894 vol-d9d6a8ae vol-8dbbc2fa vol-98c2bbef vol-ae7ed9e3 vol-5540e618 vol-9e3bbed3 vol-993bbed4 vol-a83bbee5 vol-ff52deb2)
B=(vol-175a3b54 vol-e38d0c94 vol-2a19386a vol-b846c5cf vol-98c2bbef vol-7320102b vol-8f6226cc vol-27991850 vol-71600106 vol-615e1222)

배열은 정렬되지 않았으며 중복 된 요소를 포함 할 수도 있습니다.

  1. 이 두 배열의 교차점을 만들고 다른 배열에 요소를 저장하고 싶습니다. 어떻게해야합니까?

  2. 또한 B에 표시되고 A에서는 사용할 수없는 요소 목록을 어떻게 얻을 수 있습니까?


2
이런 종류의 작업에는 쉘이 아닌 실제 프로그래밍 언어를 사용하십시오.
Stéphane Chazelas

1
요소의 순서를 유지해야합니까? 중복 된 요소가있는 경우 (예 : A 및 B 모두 foo두 번 포함 ) 결과에 복제해야합니까?
Gilles 'SO- 악의를 멈춰라

답변:


14

comm(1)는 두 목록을 비교하여 두 목록의 교차점 또는 차이점을 제공 할 수있는 도구입니다. 목록을 정렬해야하지만 쉽게 달성 할 수 있습니다.

배열을 정렬 된 목록으로 가져 오려면 다음을 수행하십시오 comm.

$ printf '%s\n' "${A[@]}" | LC_ALL=C sort

그러면 배열 A가 정렬 된 목록으로 바뀝니다. B도 똑같이하십시오.

comm교차로를 반환하는 데 사용하려면

$ comm -1 -2 file1 file2

-1 -2 file1 (A)에 고유하고 file2 (B)에 고유 한 항목을 제거한다고 말합니다.

file1 (A)이 아닌 file2 (B)에있는 내용을 리턴하게하려면 다음을 수행하십시오.

$ comm -1 -3 file1 file2

-1 -3 file1에 고유하고 두 항목에 공통적 인 항목을 제거한다고 말합니다.

에 두 개의 파이프 라인을 공급하려면 comm다음의 "프로세스 대체"기능을 사용하십시오 bash.

$ comm -1 -2 <(pipeline1) <(pipeline2)

이것을 배열로 캡처하려면 :

$ C=($(command))

함께 모아서:

# 1. Intersection
$ C=($(comm -12 <(printf '%s\n' "${A[@]}" | LC_ALL=C sort) <(printf '%s\n' "${B[@]}" | LC_ALL=C sort)))

# 2. B - A
$ D=($(comm -13 <(printf '%s\n' "${A[@]}" | LC_ALL=C sort) <(printf '%s\n' "${B[@]}" | LC_ALL=C sort)))

값에 포함되지 않은 경우에만 작동합니다 \n.
Chris Down

@ChrisDown : 맞습니다. 나는 항상 올바르게 인용되고 모든 문자를 처리하는 쉘 스크립트를 작성하려고하지만 \ n에 포기했습니다. 나는 파일 이름으로 그것을 본 적이 없으며, 많은 유닉스 도구는 \ n 구분 된 레코드로 작동하여 \ n을 유효한 문자로 처리하려고하면 많이 잃어 버립니다.
camh

1
GUI 파일 관리자를 사용할 때 다른 곳에서 복사 한 입력 파일 이름을 올바르게 위생 처리하지 않는 파일 이름에서 파일 이름을 보았습니다.
Chris Down

이것을 보호하기 위해 \n:arr1=( one two three "four five\nsix\nseven" ); arr2=( ${arr1[@]:1} "four five\\nsix" ); n1=${#arr1[@]}; n2=${#arr2[@]}; arr=( ${arr1[@]/ /'-_-'} ${arr2[@]/ /'-_-'} ); arr=( $( echo "${arr[@]}"|tr '\t' '-t-'|tr '\n' '-n-'|tr '\r' '-r-' ) ); arr1=( ${arr[@]:0:${n1}} ); arr2=( ${arr[@]:${n1}:${n2}} ); unset arr; printf "%0.s-" {1..10}; printf '\n'; printf '{'; printf " \"%s\" " "${arr1[@]}"; printf '}\n'; printf "%0.s-" {1..10}; printf '\n'; printf '{'; printf " \"%s\" " "${arr2[@]}"; printf '}\n'; printf "%0.s-" {1..10}; printf '\n\n'; unset arr1; unset arr2
Jason R. Mick

설정하지 않아야합니다 LC_ALL=C. 대신 LC_COLLATE=C다른 부작용없이 동일한 성능 향상을 설정 하십시오. 얻기 위해서는 올바른 에 대한 결과를도 같은 정렬을 설정해야합니다 comm그이 사용되었다 sort: 예unset LC_ALL; LC_COLLATE=C ; comm -12 <(printf '%s\n' "${A[@]}" | sort) <(printf '%s\n' "${B[@]}" | sort)
Sorpigal

4

두 배열을 반복하고 비교하여 A와 B에있는 모든 요소를 ​​얻을 수 있습니다.

A=(vol-175a3b54 vol-382c477b vol-8c027acf vol-93d6fed0 vol-71600106 vol-79f7970e vol-e3d6a894 vol-d9d6a8ae vol-8dbbc2fa vol-98c2bbef vol-ae7ed9e3 vol-5540e618 vol-9e3bbed3 vol-993bbed4 vol-a83bbee5 vol-ff52deb2)
B=(vol-175a3b54 vol-e38d0c94 vol-2a19386a vol-b846c5cf vol-98c2bbef vol-7320102b vol-8f6226cc vol-27991850 vol-71600106 vol-615e1222)

intersections=()

for item1 in "${A[@]}"; do
    for item2 in "${B[@]}"; do
        if [[ $item1 == "$item2" ]]; then
            intersections+=( "$item1" )
            break
        fi
    done
done

printf '%s\n' "${intersections[@]}"

B에서는 모든 요소를 ​​얻을 수 있지만 A에서는 그렇지 않습니다.

A=(vol-175a3b54 vol-382c477b vol-8c027acf vol-93d6fed0 vol-71600106 vol-79f7970e vol-e3d6a894 vol-d9d6a8ae vol-8dbbc2fa vol-98c2bbef vol-ae7ed9e3 vol-5540e618 vol-9e3bbed3 vol-993bbed4 vol-a83bbee5 vol-ff52deb2)
B=(vol-175a3b54 vol-e38d0c94 vol-2a19386a vol-b846c5cf vol-98c2bbef vol-7320102b vol-8f6226cc vol-27991850 vol-71600106 vol-615e1222)

not_in_a=()

for item1 in "${B[@]}"; do
    for item2 in "${A[@]}"; do
        [[ $item1 == "$item2" ]] && continue 2
    done

    # If we reached here, nothing matched.
    not_in_a+=( "$item1" )
done

printf '%s\n' "${not_in_a[@]}"

운동 : 당신이 교환하는 경우 AB,이다 intersections재정렬에 동일한까지 항상?
Gilles 'SO- 악의를 멈춰라

@Gilles 배열에 중복 요소가 포함될 수있는 경우 아니요.
Chris Down

3

이를 위해서는 다소 우아하고 효율적인 접근 방법이 uniq있지만, 고유 한 항목 만 남기고 각 배열에서 중복을 제거해야합니다. 중복을 저장하려면 "두 배열을 반복하고 비교하여"한 가지 방법 만 있습니다.

두 개의 배열이 있다고 가정하십시오.

A=(vol-175a3b54 vol-382c477b vol-8c027acf vol-93d6fed0 vol-71600106 vol-79f7970e vol-e3d6a894 vol-d9d6a8ae vol-8dbbc2fa vol-98c2bbef vol-ae7ed9e3 vol-5540e618 vol-9e3bbed3 vol-993bbed4 vol-a83bbee5 vol-ff52deb2)
B=(vol-175a3b54 vol-e38d0c94 vol-2a19386a vol-b846c5cf vol-98c2bbef vol-7320102b vol-8f6226cc vol-27991850 vol-71600106 vol-615e1222)

우선, 이러한 배열을 세트로 변환하십시오. 이 세트의 교차점처럼 알려진 수학 연산 교차로, 그리고 세트의 모음입니다 때문에 우리는 그것을 할 것입니다 별개의 객체, 별개의 또는 고유 한 . 솔직히 말해서, 우리가리스트 나 시퀀스에 대해 말할 때 "교차"가 무엇인지 모른다. 시퀀스에서 하위 시퀀스를 선택할 수 있지만이 작업 (선택)의 의미는 약간 다릅니다.

이제 변형하자!

$ A=(echo ${A[@]} | sed 's/ /\n/g' | sort | uniq)
$ B=(echo ${B[@]} | sed 's/ /\n/g' | sort | uniq)
  1. 교차로 :

    $ echo ${A[@]} ${B[@]} | sed 's/ /\n/g' | sort | uniq -d

    다른 배열에 요소를 저장하려면 다음을 수행하십시오.

    $ intersection_set=$(echo ${A[@]} ${B[@]} | sed 's/ /\n/g' | sort | uniq -d)
    
    $ echo $intersection_set
    vol-175a3b54 vol-71600106 vol-98c2bbef

    uniq -d의미는 복제본 만 표시한다는 것을 의미합니다 (제 생각에 uniq다소 빠르다고 생각합니다 XOR.

  2. 에 표시되고 B사용할 수없는 요소 목록을 가져옵니다 A. 즉B\A

    $ echo ${A[@]} ${B[@]} | sed 's/ /\n/g' | sort | uniq -d | xargs echo ${B[@]} | sed 's/ /\n/g' | sort | uniq -u

    또는 변수를 저장하여 :

    $ subtraction_set=$(echo ${A[@]} ${B[@]} | sed 's/ /\n/g' | sort | uniq -d | xargs echo ${B[@]} | sed 's/ /\n/g' | sort | uniq -u)
    
    $ echo $subtraction_set
    vol-27991850 vol-2a19386a vol-615e1222 vol-7320102b vol-8f6226cc vol-b846c5cf vol-e38d0c94

    따라서 처음에 우리는 ( A그리고 B단순히 그들 사이의 복제 세트 인)의 교차점을 얻었고 그것을 ( 그리고 단순히 유일한 요소 일뿐입니다)의 A/\B교차 교차 연산을 사용했습니다 .BA/\BB\A = ! (B /\ (A/\B))

PS uniq는 Richard M. Stallman과 David MacKenzie가 작성했습니다.


1

효율성을 무시하는 방법은 다음과 같습니다.

declare -a intersect
declare -a b_only
for bvol in "${B[@]}"
do
    in_both=""
    for avol in "${A[@]}"
    do
        [ "$bvol" = "$avol" ] && in_both=Yes
    done
    if [ "$in_both" ]
    then
        intersect+=("$bvol")
    else
        b_only+=("$bvol")
    fi
done
echo "intersection=${intersect[*]}"
echo "In B only=${b_only[@]}"

0

내 순수한 배쉬 방법

이 변수에는 16 진수가있는 vol-XXX위치 만 포함되므로 bash 배열을XXX 사용하는 빠른 방법이 있습니다

unset A B a b c i                    # Only usefull for re-testing...

A=(vol-175a3b54 vol-382c477b vol-8c027acf vol-93d6fed0 vol-71600106 vol-79f7970e
   vol-e3d6a894 vol-d9d6a8ae vol-8dbbc2fa vol-98c2bbef vol-ae7ed9e3 vol-5540e618
   vol-9e3bbed3 vol-993bbed4 vol-a83bbee5 vol-ff52deb2)
B=(vol-175a3b54 vol-e38d0c94 vol-2a19386a vol-b846c5cf vol-98c2bbef vol-7320102b
   vol-8f6226cc vol-27991850 vol-71600106 vol-615e1222)

for i in ${A[@]#vol-};do
    [ "${a[$((16#$i))]}" ] && echo Duplicate vol-$i in A
    ((a[$((16#$i))]++))
    ((c[$((16#$i))]++))
  done
for i in ${B[@]#vol-};do
    [ "${b[$((16#$i))]}" ] && echo Duplicate vol-$i in B
    ((b[$((16#$i))]++))
    [ "${c[$((16#$i))]}" ] && echo Present in A and B: vol-$i
    ((c[$((16#$i))]++))
  done

출력해야합니다 :

Present in A and B vol-175a3b54
Present in A and B vol-98c2bbef
Present in A and B vol-71600106

이 상태에서 bash 환경은 다음을 포함합니다.

set | grep ^c=
c=([391789396]="2" [664344656]="1" [706295914]="1" [942425979]="1" [1430316568]="1"
[1633554978]="1" [1902117126]="2" [1931481131]="1" [2046269198]="1" [2348972751]="1"
[2377892602]="1" [2405574348]="1" [2480340688]="1" [2562898927]="2" [2570829524]="1"
[2654715603]="1" [2822487781]="1" [2927548899]="1" [3091645903]="1" [3654723758]="1"
[3817671828]="1" [3822495892]="1" [4283621042]="1")

그래서 당신은 할 수 있습니다 :

for i in ${!b[@]};do
    [ ${c[$i]} -eq 1 ] &&
        printf "Present only in B: vol-%8x\n" $i
  done

렌더링됩니다 :

Present only in B: vol-27991850
Present only in B: vol-2a19386a
Present only in B: vol-615e1222
Present only in B: vol-7320102b
Present only in B: vol-8f6226cc
Present only in B: vol-b846c5cf
Present only in B: vol-e38d0c94

그러나 이것은 숫자로 정렬되어 있습니다! 원래 주문을 원할 경우 다음을 수행 할 수 있습니다.

for i in ${B[@]#vol-};do
    [ ${c[((16#$i))]} -eq 1 ] && printf "Present in B only: vol-%s\n" $i
  done

당신이 dislay 그래서 같은 순서를 submited로 :

Present in B only: vol-e38d0c94
Present in B only: vol-2a19386a
Present in B only: vol-b846c5cf
Present in B only: vol-7320102b
Present in B only: vol-8f6226cc
Present in B only: vol-27991850
Present in B only: vol-615e1222

또는

for i in ${!a[@]};do
    [ ${c[$i]} -eq 1 ] && printf "Present only in A: vol-%8x\n" $i
  done

A에만 표시 :

Present only in A: vol-382c477b
Present only in A: vol-5540e618
Present only in A: vol-79f7970e
Present only in A: vol-8c027acf
Present only in A: vol-8dbbc2fa
Present only in A: vol-93d6fed0
Present only in A: vol-993bbed4
Present only in A: vol-9e3bbed3
Present only in A: vol-a83bbee5
Present only in A: vol-ae7ed9e3
Present only in A: vol-d9d6a8ae
Present only in A: vol-e3d6a894
Present only in A: vol-ff52deb2

또는:

for i in ${!b[@]};do
    [ ${c[$i]} -eq 2 ] && printf "Present in both A and B: vol-%8x\n" $i
  done

것입니다 다시 인쇄 :

Present in both A and B: vol-175a3b54
Present in both A and B: vol-71600106
Present in both A and B: vol-98c2bbef

물론 Duplicate라인이 쓸모가 없다면 간단히 떨어 뜨릴 수 있습니다.
F. Hauri
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.