유닉스에서 파일을 정렬하지 않고 파일에서 중복 줄을 삭제하는 방법은 무엇입니까?


137

유닉스 파일에서 중복 줄을 삭제하는 방법이 있습니까?

sort -uuniq명령으로 할 수 있지만 sed또는 을 사용하고 싶습니다 awk. 가능합니까?


12
연속 복제를 의미하는 경우 uniq혼자이면 충분합니다.
Michael Krelin-해커

그렇지 않으면으로 가능 awk하지만 더 큰 파일에서 리소스를 많이 소비 할 것이라고 생각 합니다.
Michael Krelin-해커

중복 stackoverflow.com/q/24324350stackoverflow.com/q/11532157은 이상적으로 여기 마이그레이션해야 재미있는 답변이 있습니다.
tripleee

답변:


290
awk '!seen[$0]++' file.txt

seenAwk가 파일의 모든 줄을 전달할 연관 배열입니다. 행이 배열에 없으면 seen[$0]false로 평가됩니다. 는 !논리적 NOT 연산자 true로 거짓을 반전합니다. Awk는 표현식이 true로 평가되는 행을 인쇄합니다. ++증가 seen되도록 seen[$0] == 1제 시간 후에 라인하고 발견 seen[$0] == 2등.
Awk는 0""(빈 문자열)을 제외한 모든 것을 평가 합니다. 중복 라인에 배치되어있는 경우 seen다음 !seen[$0]false로 평가되고 라인은 출력에 기록되지 않습니다.


5
파일로 저장하려면 다음과 같이하십시오.awk '!seen[$0]++' merge_all.txt > output.txt
Akash Kandpal

5
여기서 중요한주의 사항 : 여러 파일에 대해이 작업을 수행해야하고 명령 끝에 더 많은 파일을 고정하거나 와일드 카드를 사용하는 경우 'seen'배열은 모든 파일의 중복 행으로 채워집니다. 대신 각 파일을 독립적으로 처리하려면 다음과 같은 작업을 수행해야합니다.for f in *.txt; do gawk -i inplace '!seen[$0]++' "$f"; done
Nick K9

@ NickK9는 여러 파일에서 누적 적으로 중복 제거하는 것이 그 자체로 훌륭하다는 점입니다. 좋은 팁
sfscs

31

에서 http://sed.sourceforge.net/sed1line.txt : (어떻게이 일을 부탁하지 마십시오 ;-))

 # delete duplicate, consecutive lines from a file (emulates "uniq").
 # First line in a set of duplicate lines is kept, rest are deleted.
 sed '$!N; /^\(.*\)\n\1$/!P; D'

 # delete duplicate, nonconsecutive lines from a file. Beware not to
 # overflow the buffer size of the hold space, or else use GNU sed.
 sed -n 'G; s/\n/&&/; /^\([ -~]*\n\).*\n\1/d; s/\n//; h; P'

괴짜 ;-) +1이지만 자원 소비는 피할 수 없습니다.
Michael Krelin-해커

3
'$! N; /^(.*)\n\1$/!P; D '는 "마지막 줄에 있지 않다면 다른 줄을 읽으십시오. 이제 가지고있는 것을 보시고 줄 바꿈 다음에 같은 줄이 뒤 따르지 않으면 다시 인쇄하십시오. 이제 삭제하십시오. 이제 삭제하십시오. "줄 바꿈까지."
Beta

2
'지; s / \ n / && /; / ^ ([-~] * \ n). * \ n \ 1 / d; s / \ n //; h; P '는 대략 "홀드 홀 전체 공간을이 줄에 추가 한 다음, 중복 된 줄이 모든 것을 버리면 전체 엉망을 홀드 스페이스에 다시 복사하고 첫 번째 부분 (인쇄 한 줄)을 인쇄합니다. 읽기 ".
베타

는 IS $!부분은 필요? 하지 않습니다 sed 'N; /^\(.*\)\n\1$/!P; D'같은 일을? 나는 내 컴퓨터에서 두 가지가 다른 예를 생각해 낼 수 없습니다 (fwiw 두 가지 버전으로 끝에 빈 줄을 시도했지만 모두 괜찮 았습니다).
eddi

1
거의 7 년 후 아무도 @amichair에 대답하지 않았다. <sniff>는 나를 슬프게한다. ;) 어쨌든 [ -~]0x20 (공백)에서 0x7E (물결표)까지의 ASCII 문자 범위를 나타냅니다. 이들은 고려 인쇄 가능한 ASCII 문자 (링크 된 페이지는 0x7F의 / 삭제가 있지만 그것은 바로 보이지 않는다). 따라서 ASCII를 사용하지 않는 사람이나 탭 문자를 사용하는 사람은 솔루션을 사용할 수 없게됩니다. 더 이식성이 좋을수록 실제로 더 많은 문자가 포함됩니다. [^\n]
B Layer

14

@jonas의 awk 솔루션과 비슷한 Perl one-liner :

perl -ne 'print if ! $x{$_}++' file

이 변형은 다음을 비교하기 전에 후행 공백을 제거합니다.

perl -lne 's/\s*$//; print if ! $x{$_}++' file

이 변형은 파일을 내부 편집합니다.

perl -i -ne 'print if ! $x{$_}++' file

이 변형은 파일을 내부 편집하고 백업합니다 file.bak

perl -i.bak -ne 'print if ! $x{$_}++' file

6

Andre Miller가 위에 게시 한 라이너는 입력 파일이 빈 줄로 끝나고 문자가없는 경우 최신 버전의 sed를 제외하고 작동합니다. 내 Mac에서 CPU가 회전합니다.

마지막 줄이 비어 있고 문자가없는 경우 무한 루프 :

sed '$!N; /^\(.*\)\n\1$/!P; D'

멈추지 않지만 마지막 줄을 잃습니다.

sed '$d;N; /^\(.*\)\n\1$/!P; D'

설명은 sed FAQ 의 맨 끝에 있습니다 .

GNU sed 관리자는 이식성 문제에도 불구하고
N 명령을 변경 (
삭제 대신 인쇄)하도록 변경하면
"다음 줄 추가"명령 이 어떻게 동작 해야하는지 에 대한 직감과 패턴 공간이 더 일관성이 있다고 생각했습니다 .
변경을 선호하는 또 다른 사실
은 파일에 홀수가있는 경우 "{N; command;}"은 마지막 라인 을 삭제하지만 파일에 짝수 개의
라인이 있으면 마지막 라인을 인쇄한다는 것입니다.

이전의 N 동작 (
EOF에 도달 할 때 패턴 공간 삭제 )을 사용한 스크립트를
모든 버전의 sed 와 호환 되는 스크립트로 변환하려면 고독한 "N;"을 변경하십시오. "$ d; N;" .


5

Vim (Vi 호환)을 사용하는 다른 방법 :

파일에서 연속 된 중복 행을 삭제합니다.

vim -esu NONE +'g/\v^(.*)\n\1$/d' +wq

파일에서 연속적이며 비 연속적인 행을 삭제합니다.

vim -esu NONE +'g/\v^(.+)$\_.{-}^\1$/d' +wq


4

첫 번째 해결책은 또한 http://sed.sourceforge.net/sed1line.txt입니다.

$ echo -e '1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5' |sed -nr '$!N;/^(.*)\n\1$/!P;D'
1
2
3
4
5

핵심 아이디어는 다음과 같습니다.

print ONLY once of each duplicate consecutive lines at its LAST appearance and use D command to implement LOOP.

설명합니다 :

  1. $!N;: 현재 행이 마지막 행이 아닌 경우 N명령을 사용 하여 다음 행을로 읽어보십시오 pattern space.
  2. /^(.*)\n\1$/!P: 전류의 내용 pattern space이 두 개로 duplicate string분리되어 \n다음 줄이 same현재 라인과 함께 있음을 의미하는 경우 핵심 아이디어에 따라 인쇄 할 수 없습니다. 그렇지 않으면, 현재 행이 모든 중복 된 연속 행의 마지막 모양임을 의미합니다. 이제 P명령을 사용 하여 현재 pattern spaceutil 에서 문자를 인쇄 할 수도 있습니다 \n( \n또한 인쇄 됨).
  3. D: 우리는 Dcommand를 사용하여 현재 pattern spaceutil 에서 문자를 삭제하고 \n( \n삭제됨) 내용은 pattern space다음 줄입니다.
  4. D명령은 강제로 sed그에게 이동 FIRST명령 $!N하지만, 파일 또는 표준 입력 스트림에서 다음 줄을 읽을 수 없습니다.

두 번째 해결책은 이해하기 쉽습니다.

$ echo -e '1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5' |sed -nr 'p;:loop;$!N;s/^(.*)\n\1$/\1/;tloop;D'
1
2
3
4
5

핵심 아이디어는 다음과 같습니다.

print ONLY once of each duplicate consecutive lines at its FIRST appearance and use : command & t command to implement LOOP.

설명합니다 :

  1. 입력 스트림 또는 파일에서 새 줄을 읽고 한 번 인쇄하십시오.
  2. 사용 :loop명령 세트 label의 이름 loop.
  3. N다음 줄을 읽는 데 사용 하십시오 pattern space.
  4. s/^(.*)\n\1$/\1/다음 행이 현재 행과 동일하면 현재 행을 삭제 하는 데 사용합니다. s명령을 사용 하여 delete작업 을 수행합니다 .
  5. s명령이 성공적으로 실행 되면 tloopcommand force sed를 사용 하여 labelnamed loop로 이동합니다. 그러면 다음 행과 동일한 루프를 수행합니다. util 행의 중복 된 연속 행은 없습니다 latest printed. 그렇지 않으면, 사용 D에 명령 delete하여와 동일 라인 latest-printed line, 그리고 힘 sed은 IS 첫 번째 명령에 이동 p명령, 현재의 내용은 pattern space다음 새로운 라인입니다.

busybox를 사용하는 Windows에서 동일한 명령 :busybox echo -e "1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5" | busybox sed -nr "$!N;/^(.*)\n\1$/!P;D"
스 캐빈 저

-1

아래 awk를 사용하여 달성 할 수 있습니다.

awk file_name | uniq

이 고유 한 값을 새 파일로 출력 할 수 있습니다

awk file_name | uniq > uniq_file_name

새 파일 uniq_file_name에는 고유 값만 포함되며 중복은 없습니다.


-4
cat filename | sort | uniq -c | awk -F" " '$1<2 {print $2}'

awk를 사용하여 중복 행을 삭제합니다.


1
이것은 라인의 순서를 방해 할 것입니다.
Vijay

1
20GB 텍스트 파일은 무엇입니까? 너무 느린.
Alexander Lubyagin

이제까지로, cat쓸모가 없다. 어쨌든 uniq이미 자체적으로 수행하며 입력이 한 줄에 정확히 하나의 단어 일 필요는 없습니다.
tripleee
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.