문자열이 특수 문자열(쉼표나 구분 기호가 아님)로 구분된 파일이 있습니다 <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의 보다 간결한 awk
cmd와 동일합니다.
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