첫 번째 열의 중복 행 삭제

첫 번째 열의 중복 행 삭제

문자열이 특수 문자열(쉼표나 구분 기호가 아님)로 구분된 파일이 있습니다 <vvv>. 예를 들어 첫 번째 필드의 모든 문자열이 고유한지 확인하고 싶습니다. 동일한 필드에 중복 행이 발견되면 중복 행을 모두 삭제하고 싶습니다(첫 번째 항목 유지).

예:

aaa<vvv>bbb<vvv>ccc
xxx<vvv>yyy<vvv>zzz
aaa<vvv>new<vvv>new2
111<vvv>222<vvv>333

난 갖길 원해:

aaa<vvv>bbb<vvv>ccc
xxx<vvv>yyy<vvv>zzz
111<vvv>222<vvv>333

이미 나타났기 aaa<vvv>new<vvv>new2때문에 제거했습니다 .aaa

awk나는 그것이 유일한 해결책이 아니라면 우리를 좋아하지 않습니다 . Linux에 익숙하지 않은 저에게는 구문이 약간 복잡합니다.

답변1

사용하지 마세요awk 매우:

$ awk -v OFS="<" '{ print NR, $0 }' file | sort -t '<' -u -k2,2 | sort -t '<' -k1,1n | cut -d '<' -f 2-
aaa<vvv>bbb<vvv>ccc
xxx<vvv>yyy<vvv>zzz
111<vvv>222<vvv>333

이는 원본 데이터에 awk행 번호를 삽입하는 데에만 사용됩니다. <이렇게 하면 원래 행의 순서를 추적할 수 있습니다. <줄 번호와 줄의 나머지 부분 사이의 구분 기호로 사용하는 이유는 원래 첫 번째 필드와 줄의 나머지 부분 사이의 구분 기호로도 나타나기 때문입니다.

파이프라인의 첫 번째 단계를 사용하여 awk행 번호를 삽입한 후 데이터는 다음과 같습니다.

1<aaa<vvv>bbb<vvv>ccc
2<xxx<vvv>yyy<vvv>zzz
3<aaa<vvv>new<vvv>new2
4<111<vvv>222<vvv>333

파이프라인의 다음 단계에서는 이를 두 번째 필드(첫 번째 원본 필드)에서 정렬하여 중복 항목을 제거합니다. 결과는 다음과 같습니다

4<111<vvv>222<vvv>333
1<aaa<vvv>bbb<vvv>ccc
2<xxx<vvv>yyy<vvv>zzz

두 번째는 sort첫 번째 필드의 행을 숫자로 정렬하여 원래 행 순서를 복원합니다.

1<aaa<vvv>bbb<vvv>ccc
2<xxx<vvv>yyy<vvv>zzz
4<111<vvv>222<vvv>333

그런 다음 cut첫 번째 필드(및 삽입된 구분 기호)에서 숫자를 제거합니다.


를 사용하지 않고 정렬된 출력을 제공하는 솔루션 awk은 다음과 같습니다.

$ sort -t '<' -u -k1,1 file
111<vvv>222<vvv>333
aaa<vvv>bbb<vvv>ccc
xxx<vvv>yyy<vvv>zzz

이는 본질적으로 위 파이프라인의 두 번째 단계로, 중복 항목을 제거하면서 첫 번째 필드의 파일을 정렬합니다.


해결책 awk은 다음과 같습니다

$ awk -F '<' '!seen[$1]++' file
aaa<vvv>bbb<vvv>ccc
xxx<vvv>yyy<vvv>zzz
111<vvv>222<vvv>333

이는 첫 번째 필드를 이름이 지정된 연관 배열의 키로 저장 seen하고 이후에 연관된 값을 증가시킵니다. 주어진 키에 대한 배열의 값이 0이면(즉, 첫 번째 필드가 이전에 본 적이 없는 경우) 해당 행을 인쇄합니다.

답변2

또는 동등하게 awk둘 다 사용하지 않고 cut다음을 사용하십시오 sed.

$ sed '=' file \
      | sed 'N;s/\n/</' \
      | sort -t"<" -u -k2,2 \
      | sort -t"<" -k1,1 \
      | sed 's/^[0-9]*<//'
aaa<vvv>bbb<vvv>ccc
xxx<vvv>yyy<vvv>zzz
111<vvv>222<vvv>333

하지만 이건매우무거운. @Kusalananda의 마지막 (awk 기반) 솔루션은 다음과 같습니다.많은더 나은 것.


교육 목적으로만 sed위의 처음 두 블록은 Kusalananda의 보다 간결한 awkcmd와 동일합니다.

  • sed '=' file, 향후 주문을 위해 라인 번호를 인쇄하세요.
  • sed 'N;s/\n/</', 패턴 공간에 다음 입력 라인을 추가하고(예: "현재 라인과 다음 라인을 연결") 라인의 끝을 로 \n바꿉니다 <.

세 번째이자 마지막 sed정보인 sed 's/^[0-9]*<//'는 이전에 각 줄의 시작 부분에 배치된 줄 번호와 "<"를 아무것도 대체하지 않습니다.


자세한 내용은 콘솔에 질문을 올려 sed주세요 .$ info sed

답변3

GNU sed를 사용하면 주어진 작업을 수행할 수 있습니다.

$ sed -Ene '
   G
   /^([^<]+)<vvv>.*\n\1(\n|$)/d
   P;s/<vvv>.*//;H
 ' input.txt

첫 번째 필드를 예약된 공간에 저장하고 이를 현재 행의 첫 번째 필드와 비교합니다. 서로 다른 경우에만 보류를 업데이트하고 현재 행을 인쇄합니다.

답변4

다음 2가지 방법을 시도했습니다.

Method1

 awk -F "<" '{if (!seen[$1]++)print }' filename

Method2

awk -F "<" '!a[$1]++' filename

산출

aaa<vvv>bbb<vvv>ccc
xxx<vvv>yyy<vvv>zzz
111<vvv>222<vvv>333

관련 정보