UNIX 쉘 스크립트의 목록에서 고유하거나 다른 값을 선택하십시오.


238

줄 바꿈으로 구분 된 긴 값 목록을 반환하는 ksh 스크립트가 있으며 고유 / 구별 값 만보 고 싶습니다. 이것을 할 수 있습니까?

예를 들어 내 출력이 디렉토리의 파일 접미사라고 가정 해보십시오.

tar
gz
java
gz
java
tar
class
class

다음과 같은 목록을보고 싶습니다.

tar
gz
java
class

답변:


432

uniqsort응용 프로그램 을보고 싶을 수 있습니다 .

./yourscript.ksh | 정렬 | 유니크

(FYI, 예,이 명령 줄에서 정렬이 필요하며 uniq서로 바로 뒤에있는 중복 줄만 제거합니다)

편집하다:

Aaron Digullauniq 의 명령 줄 옵션 과 관련하여 게시 된 내용과 달리 :

다음과 같은 입력이 주어집니다.

수업
항아리
항아리
항아리
큰 상자
큰 상자
자바

uniq 모든 줄을 정확히 한 번 출력합니다.

수업
항아리
큰 상자
자바

uniq -d 두 번 이상 나타나는 모든 줄을 출력하고 한 번 인쇄합니다.

항아리
큰 상자

uniq -u 정확히 한 번 나타나는 모든 줄을 출력하고 한 번 인쇄합니다.

수업
자바

2
후발자를위한 참고 자료 : @AaronDigulla의 답변이 수정되었습니다.
mklement0

2
아주 좋은 점은 '이 명령 행에 필요하다. 유닉스는 내가 방금 배운 바로 뒤에있는 중복 줄 만 제거한다 !!
HattrickNZ

4
GNU 는 고유 한 가치를 제공 sort하는 -u버전도 제공합니다.
Arthur2e5

나는 uniq이음새가 sort먹이기 전에 입력 할 수 있음을 의미하는 인접한 라인 만 (적어도 기본적으로) 처리 한다는 것을 알았습니다 uniq.
Stphane

85
./script.sh | sort -u

이것은 일산화탄소의 대답 과 동일 하지만 조금 더 간결합니다.


6
겸손 : 솔루션의 성능도 향상됩니다 (대규모 데이터 세트에서만 눈에 띄게 나타남).
mklement0

... | sort | uniq한 번에 수행되기 때문에 보다 효율적이어야한다고 생각합니다
Adrian Antunez

10

정렬이 바람직하지 않은 큰 데이터 세트의 경우 다음 perl 스크립트를 사용할 수도 있습니다.

./yourscript.ksh | perl -ne 'if (!defined $x{$_}) { print $_; $x{$_} = 1; }'

기본적으로 모든 라인 출력을 기억하여 다시 출력하지 않습니다.

sort | uniq정렬이 필요하지 않다는 점에서 " "솔루션 보다 장점이 있습니다 .


2
매우 큰 파일의 정렬은 그 자체로는 문제가되지 않습니다. 사용 가능한 RAM + 스왑보다 큰 파일을 정렬 할 수 있습니다. 중복이 거의없는 경우 Perl, OTOH는 실패합니다.
Aaron Digulla

1
예, 예상되는 데이터에 따라 균형이 맞습니다. Perl은 중복이 많은 대규모 데이터 세트에 적합합니다 (디스크 기반 스토리지 필요 없음). 중복이 거의없는 거대한 데이터 세트는 정렬 및 디스크 스토리지를 사용해야합니다. 작은 데이터 세트 중 하나를 사용할 수 있습니다. 개인적으로 Perl을 먼저 시도하고 실패하면 정렬로 전환합니다.
paxdiablo

정렬은 디스크로 스왑 해야하는 경우에만 이점을 제공합니다.
paxdiablo

5
이것은 모든 줄의 첫 항목을 원할 때 좋습니다. 정렬하면 문제가 발생합니다.
Bluu

10

zsh 를 사용하면 다음을 수행 할 수 있습니다.

% cat infile 
tar
more than one word
gz
java
gz
java
tar
class
class
zsh-5.0.0[t]% print -l "${(fu)$(<infile)}"
tar
more than one word
gz
java
class

또는 AWK를 사용할 수 있습니다.

% awk '!_[$0]++' infile    
tar
more than one word
gz
java
class

2
입력 정렬이 필요없는 영리한 솔루션. 주의 사항 : 매우 영리하지만 암호화 된 awk솔루션 ( 설명 은 stackoverflow.com/a/21200722/45375 참조 )은 고유 한 행 수가 충분히 적은 한 (고유 한 행이 메모리에 유지되는 한) 큰 파일에서 작동합니다. ). 이 zsh솔루션은 전체 파일을 메모리로 먼저 읽습니다. 큰 파일의 경우 옵션이 아닐 수 있습니다. 또한 작성된대로 공백이 포함되지 않은 행만 올바르게 처리됩니다. 이 문제를 해결하려면 IFS=$'\n' read -d '' -r -A u <file; print -l ${(u)u}대신 사용하십시오.
mklement0

옳은. 또는 :(IFS=$'\n' u=($(<infile)); print -l "${(u)u[@]}")
Dimitre Radoulov

1
고마워, 그것은 더 간단합니다 (서브 쉘 외부에 필요한 변수를 설정할 필요가 없다고 가정). [@]배열의 모든 요소를 ​​참조하기 위해 접미사가 필요할 때 궁금합니다. 적어도 버전 5부터는 그것없이 작동합니다. 또는 명확성을 위해 추가 했습니까?
mklement0

1
@ mklement0, 당신이 맞아요! 나는 글을 쓸 때 그것을 생각하지 않았다. 실제로, 이것으로 충분합니다 :print -l "${(fu)$(<infile)}"
Dimitre Radoulov

1
환상적입니다. 게시물을 업데이트 해 주셔서 감사합니다 awk. 샘플 출력도 자유롭게 수정했습니다 .
mklement0

9

파이프를 통해 sort하고 uniq. 이렇게하면 모든 중복이 제거됩니다.

uniq -d복제본 uniq -u만 제공하고 고유 한 복제본 만 제공합니다 (스트립 복제본).


그것의 모양에 의해 먼저 정렬
brabster

1
예, 그렇습니다 또는보다 정확하게는 모든 중복 행을 그룹화해야합니다. 정렬은 정의에 따라 이것을 수행하지만;)
Matthew Scharley

또한 uniq -u기본 동작이 아닙니다 (자세한 내용은 내 답변의 편집 참조)
Matthew Scharley

7

AWK를 사용하면 할 수있는 것보다 더 빠릅니다.

 ./yourscript.ksh | awk '!a[$0]++'

그게 제가 가장 좋아하는 일입니다. 고마워요! 특히 큰 파일의 경우 정렬 솔루션이 원하는 것이 아닐 수도 있습니다.
Schmitzi

1

요청에 따라 고유하지만 정렬되지는 않습니다.
~ 70 개 미만의 요소에 대해 적은 시간의 시스템 리소스를 사용합니다 (시간에 따라 테스트 됨).
stdin에서 입력을 받도록 작성
(또는 다른 스크립트에 수정 및 포함) :
(Bash)

bag2set () {
    # Reduce a_bag to a_set.
    local -i i j n=${#a_bag[@]}
    for ((i=0; i < n; i++)); do
        if [[ -n ${a_bag[i]} ]]; then
            a_set[i]=${a_bag[i]}
            a_bag[i]=$'\0'
            for ((j=i+1; j < n; j++)); do
                [[ ${a_set[i]} == ${a_bag[j]} ]] && a_bag[j]=$'\0'
            done
        fi
    done
}
declare -a a_bag=() a_set=()
stdin="$(</dev/stdin)"
declare -i i=0
for e in $stdin; do
    a_bag[i]=$e
    i=$i+1
done
bag2set
echo "${a_set[@]}"

0

파일에서 중복되지 않은 항목을 얻는 더 좋은 팁을 얻습니다.

awk '$0 != x ":FOO" && NR>1 {print x} {x=$0} END {print}' file_name | uniq -f1 -u
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.