해결 방법을 모르는 까다로운 문제가 있습니다.
수백만 줄의 텍스트가 포함된 텍스트 파일이 있습니다. 기본적으로 를 실행하고 싶지만 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는 그 뒤에 다음 필드를 배치하고 마지막 필드는 왼쪽에서 다시 인쇄됩니다. 오른쪽 .:
uniq
FOO
OOF
:
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
:FOO
grep
$ 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
(\1
is역참조첫 번째 캡처 그룹으로)/\(.*\)\n\1:FOO/D
- 각 쌍의 두 번째 행이 첫 번째 행과 동일하고 뒤에 가 있으면:FOO
첫D
번째 행을 삭제합니다.P
D
나머지 라인을 인쇄하고 삭제하여 다음 사이클을 시작할 준비가 되었습니다.
아니면 더 깔끔하게(@don_crissti에게 감사드립니다)
sed '$!N; /\(.*\)\n\1:FOO/!P;D' file
N
즉, 패턴 공간에는 항상 두 개의 연속된 줄이 있으며,P
sed는 두 번째 줄이 첫 번째 줄과 접미사를 더한 것과 다른 경우에만 첫 번째 줄을 인쇄합니다:FOO
. 그런 다음D
패턴 공간에서 첫 번째 줄을 제거하고 루프를 다시 시작합니다.