Devin.KR
로그인

리눅스 uniq 명령어 사용법 - 중복 제거와 빈도 세기 실전 예제

개발자 조회 1

이 명령어를 언제 쓰는가

로그에서 "어떤 값이 몇 번 나왔나"를 세는 데 쓴다. 에러 메시지 종류별 건수, IP별 요청 수, 응답 코드 분포 같은 것이 전부 uniq -c 한 줄이다. 별도의 분석 도구를 띄우지 않고 터미널에서 바로 집계할 수 있어서 장애 대응 중에 특히 유용하다.

중복 제거 용도로도 쓰지만, 단순 제거만 필요하면 sort -u 가 더 간단하다. uniq 의 진짜 가치는 -c(건수), -d(중복만), -u(유일한 것만) 세 옵션에 있다.

기본 형식

uniq [옵션] [입력파일] [출력파일]

핵심은 uniq 는 인접한 줄만 비교한다는 것이다. 파일 전체에서 중복을 찾는 것이 아니라, 바로 앞줄과 같은지만 본다. 그래서 실무에서는 거의 항상 sort | uniq 형태로 쓴다.

자주 쓰는 옵션

옵션의미예시
-c각 값이 몇 번 나왔는지 앞에 붙인다sort a.txt | uniq -c
-d두 번 이상 나온 값만 출력한다sort ids.txt | uniq -d
-u딱 한 번만 나온 값만 출력한다sort ids.txt | uniq -u
-i대소문자를 무시하고 비교한다sort -f m.txt | uniq -ci
-f N앞의 N개 필드를 빼고 비교한다uniq -f 2 log.txt
-s N앞의 N개 문자를 빼고 비교한다. 타임스탬프 무시용uniq -s 20 app.log

실전 예제

1) 접속이 많은 IP 상위 10개를 뽑는다. 리눅스 텍스트 처리의 표준 관용구다.

awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -n 10

출력은 건수, 값 순이다.

  48211 203.0.113.42
  10394 198.51.100.7
   2205 192.0.2.19

2) 어떤 종류의 에러가 얼마나 났는지 본다. 에러 하나가 폭주하는 것인지 여러 종류가 섞인 것인지 바로 판별된다.

grep 'ERROR' /var/log/app/app.log | awk -F'ERROR' '{print $2}' | sort | uniq -c | sort -rn | head

3) 데이터에 중복 키가 있는지 확인한다. DB에 유니크 인덱스를 걸기 전 반드시 하는 점검이다.

cut -d, -f1 users.csv | sort | uniq -d

아무것도 안 나오면 중복이 없다는 뜻이다.

4) 한 번만 등장한 값을 찾는다. 두 목록을 합쳐서 한쪽에만 있는 항목을 골라낼 때 쓴다.

cat old-list.txt new-list.txt | sort | uniq -u

5) 로그에서 반복되는 같은 메시지를 접는다. 같은 줄이 수천 번 찍힌 로그를 읽을 만하게 만든다.

uniq -c /var/log/app/app.log | less

여기서는 정렬하지 않는다. 시간 순서를 유지한 채 연속된 반복만 접는 것이 목적이기 때문이다.

6) 응답 코드 분포를 본다. 4xx·5xx 비율이 갑자기 오르지 않았는지 확인한다.

awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn

함정과 주의점

정렬하지 않고 uniq 를 쓰면 중복이 그대로 남는다. 가장 흔한 실수다.

$ printf 'a\nb\na\n' | uniq
a
b
a

세 줄이 그대로 나온다. 첫 번째 a 와 마지막 a 는 인접하지 않아서 중복으로 보지 않기 때문이다. sort 를 먼저 걸면 의도한 대로 동작한다.

$ printf 'a\nb\na\n' | sort | uniq
a
b

다만 앞의 5번 예제처럼 일부러 정렬하지 않는 경우도 있다. 시간 순서를 유지하면서 연속 반복만 접고 싶을 때다. 둘을 구분해서 쓴다.

-c 의 출력은 앞에 공백 패딩이 붙는다. 숫자를 오른쪽 정렬하느라 3 apple 처럼 나온다. 이걸 그대로 다음 명령에 넘기면 필드 번호가 밀린다. awk '{print $1}' 은 공백을 알아서 무시하므로 문제없지만, cut -d' ' -f1 은 빈 필드를 잡아 아무것도 못 뽑는다. uniq -c 결과를 다시 자를 때는 awk 를 쓰거나 tr -s ' ' 로 공백을 압축한다.

sort a.txt | uniq -c | sort -rn | awk '{print $2, $1}'

줄 끝 공백이나 CR 때문에 같은 값이 다르게 세어진다. 윈도우에서 만든 CSV를 처리하면 seoulseoul\r 이 별개로 집계된다. 건수를 세는데 같은 값이 두 줄로 나오면 이걸 의심한다. 앞단에서 정리한다.

tr -d '\r' < data.csv | cut -d, -f2 | sort | uniq -c

대소문자를 구분한다. ERRORError 는 다른 값으로 센다. 합쳐서 세려면 sort -f | uniq -ci 처럼 양쪽 모두에 대소문자 무시 옵션을 준다. sort 에만 주고 uniq 에 빠뜨리면 결과가 갈린다.

함께 보면 좋은 명령어

  • sort — uniq 의 전제 조건. sort -u 하나로 끝나는 경우도 많다.
  • awk — 정렬 없이 연관 배열로 세면 대용량에서 훨씬 빠르다. awk '{c[$1]++} END{for(k in c) print c[k], k}'
  • cut — 셀 대상 컬럼만 먼저 잘라내면 파이프 전체가 가벼워진다.