[linux] Bash의 배열에서 고유 한 값을 얻으려면 어떻게해야합니까?

여기 와 거의 같은 질문이 있습니다 .

aa ab aa ac aa ad등 을 포함하는 배열이 있습니다 . 이제이 배열에서 모든 고유 한 요소를 선택하고 싶습니다. 생각은,이 간단한 것 sort | uniq또는과 sort -u배열에 변화가 다른 문제에 언급, 아무것도하지만 … 코드는 다음과 같습니다

echo `echo "${ids[@]}" | sort | uniq`

내가 도대체 ​​뭘 잘못하고있는 겁니까?



답변

약간 해키하지만 이렇게해야합니다.

echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '

정렬 된 고유 결과를 다시 배열에 저장하려면 Array 할당을 수행하십시오 .

sorted_unique_ids=($(echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))

쉘이 herestrings ( bashshould)를 지원하는 경우 다음과 같이 echo변경 하여 프로세스를 절약 할 수 있습니다 .

tr ' ' '\n' <<< "${ids[@]}" | sort -u | tr '\n' ' '

입력:

ids=(aa ab aa ac aa ad)

산출:

aa ab ac ad

설명:

  • "${ids[@]}"-쉘 배열로 작업하기위한 구문 echo. @부분 수단 “어레이의 모든 요소 「
  • tr ' ' '\n'-모든 공백을 개행으로 변환합니다. 배열은 쉘에서 공백으로 구분 된 한 줄의 요소로 표시되기 때문입니다. 그리고 sort는 입력이 별도의 줄에있을 것으로 예상하기 때문입니다.
  • sort -u -고유 한 요소 만 정렬 및 유지
  • tr '\n' ' ' -앞에서 추가 한 줄 바꿈을 다시 공백으로 변환합니다.
  • $(...)명령 대체
  • Aside : tr ' ' '\n' <<< "${ids[@]}"는보다 효율적인 방법입니다.echo "${ids[@]}" | tr ' ' '\n'


답변

Bash 버전 4 이상 (최신 Linux 버전의 경우)을 실행하는 경우 원래 배열의 각 값을 포함하는 새로운 연관 배열을 만들어 bash에서 고유 한 배열 값을 가져올 수 있습니다. 이 같은:

$ a=(aa ac aa ad "ac ad")
$ declare -A b
$ for i in "${a[@]}"; do b["$i"]=1; done
$ printf '%s\n' "${!b[@]}"
ac ad
ac
aa
ad

이는 모든 배열 (모든 언어의 연관 또는 전통)에서 각 키가 한 번만 나타날 수 있기 때문에 작동합니다. 때 for루프의 두 번째 값에 도달 aaa[2]덮어 쓰고 b[aa]위해 원래 설정 하였다 a[0].

기본 bash는 일을하는 것은 파이프와 같은 외부 도구를 사용하는 것보다 빠를 수 있습니다 sort그리고 uniq당신은 등 AWK, 파이썬, 같은 더 강력한 언어를 사용하는 경우 더 큰 데이터 세트에 대한 당신이 가능성이 더 나은 성능을 볼 수 있지만,

자신감이 있다면 여러 인수에 대해 형식을 재활용하는의 기능을 for사용하여 루프를 피할 수 있습니다 . (괜찮 으면 지금 읽기를 중단하십시오.)printfeval

$ eval b=( $(printf ' ["%s"]=1' "${a[@]}") )
$ declare -p b
declare -A b=(["ac ad"]="1" [ac]="1" [aa]="1" [ad]="1" )

이 솔루션에 필요한 이유 eval는 단어 분할 전에 배열 값이 결정되기 때문입니다. 즉, 명령 대체의 출력 은 키 = 값 쌍 세트가 아니라 단일 단어 로 간주됩니다 .

이것은 서브 쉘을 사용하지만 배열 값을 처리하기 위해 bash 내장 기능 만 사용합니다. eval비판적인 눈으로 사용을 평가하십시오 . chepner 또는 glenn jackman 또는 greycat이 코드에서 오류를 찾지 못할 것이라고 100 % 확신하지 못하는 경우 대신 for 루프를 사용하십시오.


답변

이미 답변을 받았지만 검색 결과에서 상당히 높게 나타 났으며 누군가에게 도움이 될 수 있습니다.

printf "%s\n" "${IDS[@]}" | sort -u

예:

~> IDS=( "aa" "ab" "aa" "ac" "aa" "ad" )
~> echo  "${IDS[@]}"
aa ab aa ac aa ad
~>
~> printf "%s\n" "${IDS[@]}" | sort -u
aa
ab
ac
ad
~> UNIQ_IDS=($(printf "%s\n" "${IDS[@]}" | sort -u))
~> echo "${UNIQ_IDS[@]}"
aa ab ac ad
~>


답변

배열 요소에 공백이나 다른 쉘 특수 문자가있는 경우 (그렇지 않은지 확신 할 수 있습니까?) 먼저이를 캡처하려면 (항상 이렇게해야 함) 배열을 큰 따옴표로 표현하십시오! 예 : "${a[@]}". Bash는 문자 그대로 이것을 “개별 인수의 각 배열 요소”로 해석합니다 . bash 내에서 이것은 항상 항상 작동합니다.

그런 다음 정렬 된 (및 고유 한) 배열을 얻으려면 정렬이 이해하는 형식으로 변환하고이를 bash 배열 요소로 다시 변환 할 수 있어야합니다. 이것이 내가 생각 해낸 최고입니다.

eval a=($(printf "%q\n" "${a[@]}" | sort -u))

불행히도 이것은 빈 배열의 특별한 경우에 실패하여 빈 배열을 1 개의 빈 요소의 배열로 바꿉니다 (printf에 0 개의 인수가 있지만 여전히 하나의 빈 인수가있는 것처럼 인쇄하기 때문에-설명 참조). 그래서 당신은 if 또는 something에서 그것을 잡아야합니다.

설명 : printf의 % q 형식은 bash가 eval과 같이 복구 할 수있는 것과 같은 방식으로 인쇄 된 인수를 “이스케이프”합니다. 각 요소는 자체 줄에서 이스케이프 처리 된 셸로 인쇄되기 때문에 요소 사이의 유일한 구분 기호는 개행이며 배열 할당은 각 줄을 요소로 사용하여 이스케이프 된 값을 리터럴 텍스트로 구문 분석합니다.

예 :

> a=("foo bar" baz)
> printf "%q\n" "${a[@]}"
'foo bar'
baz
> printf "%q\n"
''

eval은 어레이로 돌아가는 각 값에서 이스케이프를 제거하는 데 필요합니다.


답변

‘sort’는 for 루프의 출력을 정렬하는 데 사용할 수 있습니다.

for i in ${ids[@]}; do echo $i; done | sort

“-u”로 중복 제거 :

for i in ${ids[@]}; do echo $i; done | sort -u

마지막으로 고유 한 요소로 배열을 덮어 쓸 수 있습니다.

ids=( `for i in ${ids[@]}; do echo $i; done | sort -u` )


답변

이것은 또한 순서를 유지합니다.

echo ${ARRAY[@]} | tr [:space:] '\n' | awk '!a[$0]++'

고유 값으로 원래 배열을 수정하려면 다음을 수행하십시오.

ARRAY=($(echo ${ARRAY[@]} | tr [:space:] '\n' | awk '!a[$0]++'))


답변

고유 한 값으로 구성된 새 배열을 만들려면 배열이 비어 있지 않은지 확인한 후 다음 중 하나를 수행하십시오.

중복 항목 제거 (정렬 사용)

readarray -t NewArray < <(printf '%s\n' "${OriginalArray[@]}" | sort -u)

중복 항목 제거 (정렬하지 않음)

readarray -t NewArray < <(printf '%s\n' "${OriginalArray[@]}" | awk '!x[$0]++')

경고 : NewArray=( $(printf '%s\n' "${OriginalArray[@]}" | sort -u) ). 공백에서 깨집니다.