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

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

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



답변

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로 평가되고 라인은 출력에 기록되지 않습니다.


답변

에서 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'


답변

@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


답변

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;” .


답변

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

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

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

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

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


답변

첫 번째 해결책은 또한 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다음 새로운 라인입니다.

답변

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

awk file_name | uniq

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

awk file_name | uniq > uniq_file_name

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