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


93

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

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

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

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

답변:


131

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

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'

37
+1. 좀 더 깔끔하게 : 고유 한 요소를 새 어레이에 저장 :uniq=($(printf "%s\n" "${ids[@]}" | sort -u)); echo "${uniq[@]}"
glenn jackman

트윗 담아 가기 난 당신이 사용할 수있는 몰랐 printf그런 식으로 (형식 문자열보다 더 많은 인수를 줄)
샘슨 첸

4
+1 이것이 분리 된 경우인지 확실하지 않지만 고유 항목을 배열에 다시 넣으려면 다음과 같은 추가 괄호가 필요합니다 sorted_unique_ids=($(echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ')). 추가 괄호가 없으면 문자열로 제공되었습니다.
whla

3
요소의 순서를 변경하지 않으려면 ... | uniq | ...대신을 사용하십시오 ... | sort -u | ....
Jesse Chisholm

2
@Jesse uniq연속 된 중복 만 제거합니다 . 이 답변의 예에서는 sorted_unique_ids원본과 동일하게 끝납니다 ids. 순서를 유지하려면을 시도하십시오 ... | awk '!seen[$0]++'. stackoverflow.com/questions/1444406/… 도 참조하십시오 .
Rob Kennedy

29

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 루프를 사용하십시오.


오류 생성 : 식 재귀 수준 초과
Benubird 2014

1
@Benubird-터미널 내용을 붙여 넣을 수 있습니까? 그것은 나에게 완벽하게 작동하므로 (1) 오타, (2) 이전 버전의 bash (v4에 연관 배열이 추가됨) 또는 (3) 엄청나게 많은 우주 배경 유입이 있습니다. 이웃 지하실의 양자 블랙홀에 의해 발생하는 방사선으로 컴퓨터 내의 신호와 간섭을 일으 킵니다.
ghoti 2014

1
작동하지 않는 것을 보관할 수 없습니다. 하지만, 방금 당신의 것을 실행 해 보았고 효과가있었습니다. 그래서 아마도 우주 방사선 문제 일 것입니다.
Benubird 2014

이 답변은 bash v4 (연관 배열)를 사용하고 누군가 bash v3에서 시도하면 작동하지 않습니다 (아마 @Benubird가 본 것이 아님). Bash v3는 여전히 많은 환경에서 기본값입니다
nhed

1
@nhed, 포인트 촬영. Macports에서 v4를 설치했지만 최신 Yosemite Macbook의 기본 버전은 동일합니다. 이 질문에는 "linux"라는 태그가 지정되어 있지만 요구 사항을 지적하기 위해 답변을 업데이트했습니다.
ghoti 2015

18

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

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
~>

1
: 나는이 작업을 수행하도록 강요했다 배열를 해결하기 위해 ids=(ab "a a" ac aa ad ac aa);IFS=$'\n' ids2=(`printf "%s\n" "${ids[@]}" |sort -u`)내가 추가 된, 그래서 IFS=$'\n'@gniourf_gniourf 제안
물병 전원을

나는 또한 백업하고 명령 후에 IFS 값을 복원해야했습니다! 아니면 다른 일을 망쳐 놨 ..
물병 전원

@Jetse 이것은 두 개의 명령, 루프 없음, 평가 없음을 사용하고 가장 컴팩트 한 버전이므로 허용되는 답변이어야합니다.
mgutt

1
@AquariusPower 조심스럽게, 당신은 기본적으로 : IFS=$'\n'; ids2=(...), 변수 할당 이전의 임시 할당은 불가능하기 때문입니다. 대신 다음 구성을 사용하십시오 IFS=$'\n' read -r -a ids2 <<<"$(printf "%s\n" "${ids[@]}" | sort -u)"..
Yeti

13

배열 요소에 공백이나 다른 쉘 특수 문자가있는 경우 (그렇지 않은지 확신 할 수 있습니까?) 먼저이를 캡처하려면 (항상 이렇게해야 함) 배열을 큰 따옴표로 표현하십시오! 예 : "${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은 어레이로 돌아가는 각 값에서 이스케이프를 제거하는 데 필요합니다.


이것은 내 문자열 배열에 공백이 있기 때문에 나를 위해 일한 유일한 코드입니다. % q는 트릭을 한 것입니다. 감사합니다 :)
Somaiah Kumbera

요소의 순서를 변경하지 않으려면 uniq대신을 사용하십시오 sort -u.
Jesse Chisholm

참고 uniq가 항상와 함께 사용되어야하므로, 정렬되지 않은 목록에서 제대로 작동하지 않습니다 sort.
Jean Paul

정렬되지 않은 목록의 uniq는 연속 중복 을 제거 합니다. 다른 요소로 구분 된 동일한 목록 요소는 제거되지 않습니다. uniq는 예상되는 데이터와 원래 순서를 유지하려는 욕구에 따라 충분히 유용 할 수 있습니다.
vontrapp

10

'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` )

당신이 남아 있는지의 순서를 변경하지 않으려면, 당신은 필요 없어 :ids=( `for i in ${ids[@]}; do echo $i; done | uniq` )
제시 치솜

3

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

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

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

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

사용하지 마십시오 uniq. 정렬이 필요하지만 awk는 그렇지 않습니다.이 답변의 목적은 입력이 정렬되지 않은 경우 순서를 유지하는 것입니다.
bukzor

2

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

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

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) ). 공백에서 깨집니다.


중복 항목 제거 (정렬 없음)는 변경 사항 sort -u을 제외하고는 (정렬 포함)과 같습니다 uniq.
Jesse Chisholm

@JesseChisholm uniq은 인접한 중복 줄만 병합하므로 awk '!x[$0]++'.
6

@JesseChisholm 오해의 소지가있는 댓글을 삭제하십시오.
bukzor

2

고양이 number.txt

1 2 3 4 4 3 2 5 6

열에 줄 인쇄 : cat number.txt | awk '{for(i=1;i<=NF;i++) print $i}'

1
2
3
4
4
3
2
5
6

중복 기록 찾기 : cat number.txt | awk '{for(i=1;i<=NF;i++) print $i}' |awk 'x[$0]++'

4
3
2

중복 레코드 교체 : cat number.txt | awk '{for(i=1;i<=NF;i++) print $i}' |awk '!x[$0]++'

1
2
3
4
5
6

Uniq 레코드 만 찾기 : cat number.txt | awk '{for(i=1;i<=NF;i++) print $i|"sort|uniq -u"}

1
5
6

1

원래 주문을 잃지 않고 :

uniques=($(tr ' ' '\n' <<<"${original[@]}" | awk '!u[$0]++' | tr '\n' ' '))

1

bash 내부 만 사용하는 솔루션을 원하는 경우 값을 연관 배열의 키로 설정 한 다음 키를 추출 할 수 있습니다.

declare -A uniqs
list=(foo bar bar "bar none")
for f in "${list[@]}"; do 
  uniqs["${f}"]=""
done

for thing in "${!uniqs[@]}"; do
  echo "${thing}"
done

이것은 출력됩니다

bar
foo
bar none

나는 이것이 본질적으로 위의 @ghotis 대답과 동일하다는 것을 알았습니다. 단 그의 솔루션은 공백이있는 목록 항목을 고려하지 않습니다.
rln

좋은 지적. 내 솔루션에 따옴표를 추가하여 이제 공백을 처리합니다. 나는 원래 질문의 샘플 데이터를 처리하기 위해 작성했지만 이와 같은 우발적 인 상황을 다루는 것이 항상 좋습니다. 제안 해 주셔서 감사합니다.
ghoti

1

포함 된 공백을 처리하는 또 다른 옵션은로 널 구분 printf, 로 구분 sort한 다음 루프를 사용하여 다시 배열로 압축하는 것입니다.

input=(a b c "$(printf "d\ne")" b c "$(printf "d\ne")")
output=()

while read -rd $'' element
do 
  output+=("$element")
done < <(printf "%s\0" "${input[@]}" | sort -uz)

이 끝나면 input하고 output원하는 값을 포함하는 (단, 순서는 중요하지 않다)

$ printf "%q\n" "${input[@]}"
a
b
c
$'d\ne'
b
c
$'d\ne'

$ printf "%q\n" "${output[@]}"
a
b
c
$'d\ne'

1

이 변형은 어떻습니까?

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

그리고 sorted_arr=($(printf '%s\n' "${ids[@]}" | sort -u).
조류

0

파일의 첫 번째 열에 대한 고유 값을 얻으려면 이것을 시도하십시오.

awk -F, '{a[$1];}END{for (i in a)print i;}'

-3
# Read a file into variable
lines=$(cat /path/to/my/file)

# Go through each line the file put in the variable, and assign it a variable called $line
for line in $lines; do
  # Print the line
  echo $line
# End the loop, then sort it (add -u to have unique lines)
done | sort -u
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.