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 ( bash
should)를 지원하는 경우 다음과 같이 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' ' '
-앞에서 추가 한 줄 바꿈을 다시 공백으로 변환합니다.$(...)
- 명령 대체tr ' ' '\n' <<< "${ids[@]}"
는보다 효율적인 방법입니다.echo "${ids[@]}" | tr ' ' '\n'
printf
그런 식으로 (형식 문자열보다 더 많은 인수를 줄)
sorted_unique_ids=($(echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))
. 추가 괄호가 없으면 문자열로 제공되었습니다.
... | uniq | ...
대신을 사용하십시오 ... | sort -u | ...
.
uniq
는 연속 된 중복 만 제거합니다 . 이 답변의 예에서는 sorted_unique_ids
원본과 동일하게 끝납니다 ids
. 순서를 유지하려면을 시도하십시오 ... | awk '!seen[$0]++'
. stackoverflow.com/questions/1444406/… 도 참조하십시오 .
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
루프의 두 번째 값에 도달 aa
에 a[2]
덮어 쓰고 b[aa]
위해 원래 설정 하였다 a[0]
.
기본 bash는 일을하는 것은 파이프와 같은 외부 도구를 사용하는 것보다 빠를 수 있습니다 sort
그리고 uniq
당신은 등 AWK, 파이썬, 같은 더 강력한 언어를 사용하는 경우 더 큰 데이터 세트에 대한 당신이 가능성이 더 나은 성능을 볼 수 있지만,
자신감이 있다면 여러 인수에 대해 형식을 재활용하는의 기능을 for
사용하여 루프를 피할 수 있습니다 . (괜찮 으면 지금 읽기를 중단하십시오.)printf
eval
$ 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
~>
ids=(ab "a a" ac aa ad ac aa);IFS=$'\n' ids2=(`printf "%s\n" "${ids[@]}" |sort -u`)
내가 추가 된, 그래서 IFS=$'\n'
@gniourf_gniourf 제안
IFS=$'\n'; ids2=(...)
, 변수 할당 이전의 임시 할당은 불가능하기 때문입니다. 대신 다음 구성을 사용하십시오 IFS=$'\n' read -r -a ids2 <<<"$(printf "%s\n" "${ids[@]}" | sort -u)"
..
배열 요소에 공백이나 다른 쉘 특수 문자가있는 경우 (그렇지 않은지 확신 할 수 있습니까?) 먼저이를 캡처하려면 (항상 이렇게해야 함) 배열을 큰 따옴표로 표현하십시오! 예 : "${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은 어레이로 돌아가는 각 값에서 이스케이프를 제거하는 데 필요합니다.
uniq
대신을 사용하십시오 sort -u
.
uniq
가 항상와 함께 사용되어야하므로, 정렬되지 않은 목록에서 제대로 작동하지 않습니다 sort
.
'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` )
고유 한 값으로 구성된 새 배열을 만들려면 배열이 비어 있지 않은지 확인한 후 다음 중 하나를 수행하십시오.
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
.
uniq
은 인접한 중복 줄만 병합하므로 awk '!x[$0]++'
.
고양이 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
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
포함 된 공백을 처리하는 또 다른 옵션은로 널 구분 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'
uniq=($(printf "%s\n" "${ids[@]}" | sort -u)); echo "${uniq[@]}"