거의 중복된 행 제거

거의 중복된 행 제거

해결 방법을 모르는 까다로운 문제가 있습니다.

수백만 줄의 텍스트가 포함된 텍스트 파일이 있습니다. 기본적으로 를 실행하고 싶지만 uniq약간의 변형이 있습니다. 두 줄이 동일하지만 접미사가 있는 경우 :FOO접미사가 누락된 줄을 삭제합니다. 하지만오직그렇지 않으면 행이 동일한 경우. 그리고오직for :FOO, 다른 가능한 접미사 대신. 하다아니요/usr/bin/delta:FOO위 행이 동일하지 않아 삭제하고 싶습니다 .

red.7
green.2
green.2:FOO
blue.6
yellow.9:FOO

green.2아래 줄은 동일하지만 접미사가 붙어서 삭제하고 싶습니다 . 다른 모든 행은 변경되지 않은 상태로 유지되어야 합니다.

[편집하다:파일이 이미 순서대로 정리되어 있다는 것을 언급하는 것을 잊어버렸습니다. ]

지금까지 내 생각:

  • 분명히 uniq이를 수행하는 도구입니다.
  • uniq하나는 무시해 도 돼접두사, 하지만 결코접미사. (이건 매우 짜증나는 일이다!)
  • 나는 그것이 :필드 구분자인 것처럼 가장하고 cut(와 함께 paste) 필드 순서를 뒤집을 수 있다고 생각했습니다. 그러나 아니요, cut구분 기호가 없으면 빈 줄을 강제로 출력하는 것은 분명히 불가능합니다.
  • 다음 아이디어는 행을 단계별로 진행하여 접미사가 있는지 여부에 따라 1문자 접두사를 출력하는 것입니다. 하지만 이것을 고성능 Bash 루프로 작성하는 것은 상상할 수 없습니다.

어떤 팁이 있나요?

그냥 사용하게 될 수도 있어요진짜이 문제를 해결하기 위한 프로그래밍 언어 Bash에서 수정하면 충분히 간단해 보이지만 제대로 작동하지 못해서 많은 시간을 낭비했습니다...

답변1

가장 간단한 경우, 없이 행을 유지하려면 :FOO삭제한 다음 uniq를 전달하면 됩니다 :FOO.

$ sed 's/:FOO$//' file | uniq
red.7
green.2
blue.6
yellow.9

줄을 유지 :FOO하고 접미사가 없는 형제 뒤에 항상 온다고 가정하는 경우 다음을 시도해 볼 수 있습니다.

$ rev file | sed 's/:/ /' | uniq -f1 | sed 's/ /:/' | rev
red.7
green.2:FOO
blue.6
yellow.9:FOO

rev각 줄을 오른쪽에서 왼쪽으로 인쇄합니다. 첫 번째 필드를 공백으로 바꾸면 sed인식(또는 이 경우)을 무시해야 하는 첫 번째 필드로 사용할 수 있습니다. 다음 sed는 그 뒤에 다음 필드를 배치하고 마지막 필드는 왼쪽에서 다시 인쇄됩니다. 오른쪽 .:uniqFOOOOF:rev


불행하게도 문서에서 주장하는 내용에도 불구하고 uniq필드 구분 기호로 공백과 탭뿐만 아니라 영숫자가 아닌 거의 모든 문자를 사용합니다.

$ printf 'foo/1\nfoo/2\nfoo%%3\nfoo:4\n' 
foo/1
foo/2
foo%3
foo:4
$ printf 'foo/1\nfoo/2\nfoo%%3\nfoo:4\n'  | uniq -f1
foo/1

이는 해당 문자가 있으면 위의 해결 방법이 작동하지 않음을 의미합니다. 대안으로 grep파일의 모든 인스턴스를 제거하고 결과를 새 인스턴스에 패턴 목록으로 제공하여 다음을 방지할 수 있습니다.:FOO:FOOgrep

$ grep -hFxv "$(grep ':FOO' file | cut -d: -f1)" file 
red.7
green.2:FOO
blue.6
yellow.9:FOO

답변2

떨어져 있는 awk:

awk '$0 != x ":FOO" && NR>1 {print x} {x=$0} END {print}' file

줄을 저장한 다음 각 줄의 시작 부분에서 저장된 문자열 + 가 포함되어 있지 않은지 확인하세요 :FOO. 다음 줄이 없기 때문에 마지막 줄을 인쇄하세요 :FOO.

답변3

인접한 줄 쌍을 연결한 다음 역참조를 사용하여 고유하지 않은 접두사를 찾으려면 어떻게 해야 합니까?

$ sed '$!N; /\(.*\)\n\1:FOO/D; P;D' file
red.7
green.2:FOO
blue.6
yellow.9:FOO

설명하다:

  • $!N- 아직 마지막 줄에 도달하지 않았다면 패턴 공간에 다음 줄을 줄바꿈으로 구분하여 추가합니다.
  • /\(.*\)\n- 개행 문자(즉, 각 줄 쌍의 첫 번째 줄)까지 모든 항목을 일치시키고 다음 위치에 저장합니다.캡처 그룹
  • \1:FOO이제 첫 번째 줄에서 캡처된 모든 항목과 일치하며 그 뒤에는 :FOO( \1is역참조첫 번째 캡처 그룹으로)
  • /\(.*\)\n\1:FOO/D- 각 쌍의 두 번째 행이 첫 번째 행과 동일하고 뒤에 가 있으면 :FOOD번째 행을 삭제합니다.
  • PD나머지 라인을 인쇄하고 삭제하여 다음 사이클을 시작할 준비가 되었습니다.

아니면 더 깔끔하게(@don_crissti에게 감사드립니다)

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

N즉, 패턴 공간에는 항상 두 개의 연속된 줄이 있으며, Psed는 두 번째 줄이 첫 번째 줄과 접미사를 더한 것과 다른 경우에만 첫 번째 줄을 인쇄합니다 :FOO. 그런 다음 D패턴 공간에서 첫 번째 줄을 제거하고 루프를 다시 시작합니다.

관련 정보