특정 열에서 중복 필드 제거

특정 열에서 중복 필드 제거

특정 열(예에서는 $2)에서 중복 필드(쉼표로 구분)를 제거하고 싶습니다.

입력 파일:

A    1,2,3,4   
B    4,5,6,3
C    2,15

예상 출력:

A    1,2,3,4
B    5,6
C    15

답변1

perl -lpe 's/\s\K\S+/join ",", grep {!$seen{$_}++} split ",", $&/e'

위 코드를 다음과 같이 실행할 수 있습니다.

$ perl -lpe 's/\s\K\S+/join ",", grep {!$seen{$_}++} split ",", $&/e' afile 
A    1,2,3,4
B    5,6
C    15

어떻게 작동하나요?

를 처음 호출 perl하면 -lpe다음 세 가지 작업이 수행됩니다.

  • -l[octal] 줄 끝 처리를 활성화하고 줄 종결자를 지정합니다.
  • -p -n과 같은 반복을 가정하고 sed와 같은 라인 인쇄도 가정합니다.
  • -e program 한 줄 프로그램(여러 -e 허용, 프로그램 파일 생략)

이는 본질적으로 파일을 가져와 개행 문자를 제거하고 한 줄에서 작동한 다음 작업이 완료되면 다시 개행 문자를 추가합니다. 따라서 파일을 반복하면서 각 파일에 대해 Perl 코드를 차례로 실행합니다.

실제 Perl 코드는 다음과 같습니다.

  • \s 공백 문자( 예: 최신 버전에서는 [ \f\n\r\t]5 자 )를 나타냅니다.\vperl[[:space:]]
  • \K 내용을 \K 왼쪽에 유지하고 $&에 포함하지 마세요.
  • \S+ 세트에 없는 하나 이상의 문자 [\f\n\r\t\v]

결과가 수집 되고 join ",",각 필드가 쉼표로 구분되도록 다시 결합됩니다.

split ",", $&에서 찾은 일치 항목을 가져와서 \S+쉼표 없이 필드로만 분할합니다.

각 필드의 번호를 가져 와서 grep {!$seen{$_}++}해시에 추가합니다. $seen{}여기서 각 필드의 번호는 $_각 필드를 반복할 때의 번호입니다. 필드 번호가 "표시"될 때마다 ++연산자를 통해 계산 됩니다 $seen{$_}++.

grep{!$seen{$_}++}필드 값이 한 번만 발생하면 필드 값이 반환됩니다.

수정하고 무슨 일이 일어나는지 확인하세요

이 수정된 혐오스러운 기능을 사용하면 Perl 코드의 한 줄이 파일에서 여러 줄로 이동할 때 어떤 일이 발생하는지 확인할 수 있습니다.

$ perl -lpe 's/\s\K\S+/join ",", grep {!$seen{$_}++} split ",", $&/e; @a=keys %seen; @b=values %seen; print "keys: @a | vals: @b"' afile 
keys: 4 1 3 2 | vals: 1 1 1 1
A    1,2,3,4
keys: 6 4 1 3 2 5 | vals: 1 2 1 2 1 1
B    5,6
keys: 6 4 1 3 2 15 5 | vals: 1 2 1 2 2 1 1
C    15

$seen{}이는 처리된 파일의 줄 끝에 무엇이 있는지 보여줍니다. 파일의 두 번째 줄을 살펴보겠습니다.

B    4,5,6,3

다음은 다음과 같이 줄을 표시하는 수정된 버전입니다.

keys: 6 4 1 3 2 15 5 | vals: 1 2 1 2 2 1 1

즉, 필드 #6(1회), 필드 #4(2회) 등, 필드 #5(1회)를 확인했다는 의미입니다. 따라서 grep{...}결과가 반환될 때 해당 배열의 결과가 이 행(4,5,6,3)에 나타나고 1회(6,1,15,5)만 표시되는 경우 해당 배열의 결과만 반환됩니다. 이 두 목록의 교집합은 (5,6)이고 이것이 반환됩니다 grep.

인용하다

답변2

{ 
    tr -cs '0-9ABC' '[\n*]' | 
    nl -w1 -s: |
    sort -t: -uk2,2 | sort -t: -k1,1n |
    sed 's/[^:]*://;/^[ABC]/!{H;$!d
        };x;y/\n/,/;s/,/\t/'

} <<\FILE
A       1,2,3,4
B       4,5,6,3
C       2,15
FILE

기본적으로 데이터가 실행되고 sort -u전송되었을 때와 상대적으로 동일한 형태로 반환되는지 확인하기 위해 몇 가지 단계를 수행하는 것입니다.

First는 tr숫자가 아닌 입력의 모든 바이트를 ABC줄 바꿈으로 변환하고 모든 반복을 압축합니다. 따라서 각 필드에는 자체 행이 있습니다.

nl그런 다음 입력의 각 행에 번호를 매깁니다. 출력은 다음과 같습니다.

1:A
2:1
3:2

...등.

다음으로 데이터가 sort두 번 편집됩니다. 첫 번째는 sort모든 중복 필드를 제거합니다. 각 행의 두 번째 데이터 필드에서만 작동하고 이를 로 바꿉니다 . 두 번째는 :이번에 첫 번째 필드( 번호가 매겨진 시퀀스) 를 지정하여 sort원래 순서를 복원합니다 .nlsort

마지막으로 sed모든 것을 다시 합치십시오. 입력을 수집하고, 모든 nl삽입을 제거하고, xABC$마지막 행의 보류 및 모드 버퍼를 변경합니다. 각 줄에서 y모든 ewline을 쉼표 \n로 변환하고 각 줄의 첫 번째 줄을 마지막 a로 바꿉니다.,<tab>

결과는 어떻습니까?

산출

A       1,2,3,4
B       5,6
C       15

친구 여러분, 이것이 바로 유닉스의 아름다움입니다.

그리고 궁금하기 때문에 sed개별 항목은 다음과 같습니다.

sed 's/\t\(.*\)/,\1,/;H;$!d;x;:t
     s/,\([^,]*,\)\(.*,\)\1/,\1\2/;tt
     s/,\(\n.\),/\1\t/g;s/,\(.*\),/\t\1/'
' <<\FILE
A       1,2,3,4
B       4,5,6,3
C       2,15
FILE

첫 번째와 세 번째 줄은 준비/정리와 같습니다. 첫 번째는 전체 파일을 패턴 공간으로 가져오고 각 필드가 쉼표로 구분되어 있는지 확인합니다. 예를 들어 첫 번째 줄은 다음과 같습니다.

A,1,2,3,4,

지나고 나면 이런 모습입니다.

두 번째 행은 모든 작업이 완료되는 곳입니다. 이는 실제로 재귀 교체 기능입니다. 더 이상 필드를 찾을 수 없을 때까지 다른 곳에서만 일치할 수 있는 쉼표로 구분된 필드를 계속 교체합니다. 이것이 est 명령이 수행하는 작업입니다 t.

내가 말했듯이 세 번째 줄은 모든 것을 정리합니다. 쉼표와 탭 등을 원래 위치로 되돌리고 최종 결과는 다음과 같습니다.

산출

A       1,2,3,4
B       5,6
C       15

재미있지만, 상당한 양의 데이터에 대해 이 작업을 수행하는 것은 컴퓨터 살인입니다. 이는 재귀 정규 표현식으로, 아마도 최악의 재귀 유형일 수 있습니다. 따라서 단 몇 줄의 코드만으로 이런 종류의 작업을 수행하는 것이 가능하지만 이런 식으로 스프레드시트를 만드는 것은 아마도 현명하지 못할 것입니다.

답변3

그걸로 awk사용하기 쉽습니다array,split, 뿐만 아니라 정규loop:

{
    split($2, elements, ",")
    out = ""
    for (i in elements) {
        el = elements[i]
        if (!(el in used)) {
            out = out el ","
        }
        used[el] = 1
    }
    sub(/,$/, "", out)
    $2 = out
}
1

각 행에 대해 두 번째 열을 쉼표로 구분하고 비트를 배열에 저장합니다 elements. 그런 다음 루프를 사용하여 해당 열에 대한 새 값을 작성하고 이전에 해당 값을 본 적이 있는지 확인합니다. (연관) 배열에서 이미 본 값 세트를 유지합니다 used. 이전에 본 적이 있는 경우 el in used출력에 넣지 않아야 합니다. 그렇지 않으면 새로운 것이므로 out다시 사용하지 않도록 연결하여 본 값 집합에 추가합니다. 마지막으로 구성된 목록을 다시 두 번째 열에 넣습니다. 이는 본질적으로 다른 언어에서 취하는 접근 방식입니다.

위 코드를 파일에 넣고 awk -f또는 작은따옴표를 사용하여 명령줄에서 인수로 모두 실행합니다.

관련 정보