여러 파일에서 테이블 만들기

여러 파일에서 테이블 만들기

다음 형식으로 탭으로 구분된 여러 개의 2열 파일을 병합하고 싶습니다.

  a
A 5
C 4  
D 2

  b
A 2
B 5
C 3

  c
B 4
C 4
D 2

다음 형식으로 단일 테이블에 넣습니다.

  a b c
A 5 2 0
B 0 5 4
C 4 3 4
D 2 0 2

답변1

join작동하는 도구이지만 옵션이 약간 짜증스럽습니다.

join -t $'\t' -a1 -a2 -o 0,1.2,2.2     file1 file2 |
join -t $'\t' -a1 -a2 -o 0,1.2,1.3,2.2     - file3 |
sed 's/\t\(\t\|$\)/\t0\1/g'
    a   b   c
A   5   2   0
B   0   5   4
C   4   3   4
D   2   0   2

해당 옵션을 먼저 사용했지만 -e이로 인해 헤더 행에 문제가 발생했습니다.

답변2

본질적으로 값의 2D 배열을 만들고 싶습니다. 각 행의 첫 번째 열은 다음과 같습니다.열쇠, 각 입력 파일의 각 줄에서 탭으로 구분된 첫 번째 필드에서 가져옵니다. 아래의 각 열은 별도의 입력 파일에 해당합니다.

awk 'BEGIN {
         RS = "(\r\n|\n\r|\r|\n)"
         FS = " *\t *"
         SUBSEP = ":"
     }
     FNR==1 {
         ++file
     }
     NF>=2 {
         if ($1 in keynum)
             key = keynum[$1]
         else {
             key = ++keys
             keynum[$1] = key
             keystr[key] = $1
         }
         value[key,file] = $2
     }
     END {
         files = file
         for (key = 1; key <= keys; key++) {
             printf "%s", keystr[key]
             for (file = 1; file <= files; file++)
                 printf "\t%s", value[key,file]
             printf "\n"
         }
     }' INPUT1 INPUT2 ... INPUTN

BEGIN규칙은 각 행이 별도의 레코드가 되도록 레코드 구분 기호를 모든 유형의 개행 문자로 설정합니다. 또한 주변의 공백을 포함하여 필드 구분 기호를 탭 문자로 설정합니다.

awk에서 모든 배열은 연관되어 있으며 본질적으로 1차원입니다. 다차원 배열은 인덱스를 연결하여 SUBSEP지원됩니다 (중간에 하나 포함). 여기서는 :사용된 인덱스가 양의 정수이기 때문에 구분 기호로 사용합니다. (원하는 경우 tab 과 같은 다른 많은 문자를 사용할 수 있습니다 \t.)

FNR==1규칙은 각 입력 파일의 첫 번째 줄에서 실행됩니다. 첫 번째 입력 파일, 두 번째 입력 파일 등에 적용 file되도록 변수를 추가합니다 .12

NF>=2이 규칙은 필드가 두 개 이상 있는 모든 레코드에 대해 트리거됩니다. 이 경우 한 줄에 하나의 탭 문자가 있다는 의미입니다. 첫 번째 필드는열쇠, 두 번째 필드.

이 변수는 key고유 키 문자열을 참조하는 양의 정수입니다. (1은 첫 번째 고유한열쇠모든 입력 파일에서 2에서 두 번째 파일까지 표시됩니다. )

연관 배열은 keynum키 문자열을 키 번호( key, 양의 정수)에 매핑합니다. 이는 keystr키 번호를 키 문자열에 매핑하는 역 매핑입니다.

NF>=2규칙 에서 첫 번째 필드가 이미 알려진 키인 경우 해당 번호를 찾으세요. 그렇지 않으면 첫 번째 필드가 새 고유 키 문자열로 추가됩니다. 그런 다음 두 번째 필드가 value배열에 저장됩니다.

END이 규칙은 모든 입력 파일이 처리된 후에 트리거됩니다. 배열 value은 우리가 원하는 필드를 포함하는 논리적 2D 배열입니다.

외부 루프는 key처음 표시된 순서대로 표시된 모든 고유 키를 반복합니다. 외부 루프를 반복할 때마다 출력 행이 생성됩니다.

내부 루프는 file각 입력 파일을 나열된 순서대로 반복합니다. 각 반복은 현재 출력 행에 추가 열을 생성합니다. 각 출력 행에는 지정된 입력 파일 수보다 정확히 하나 더 많은 열이 포함됩니다. (입력 파일이 지정되지 않으면 awk는 표준 입력에서 읽고 이를 입력 파일로 처리합니다.)

이것은 확실히 이를 달성하는 가장 쉬운 방법은 아니지만 강력하기 때문에 이것을 좋아합니다(Unix, Linux, 이전 Mac, 새 Mac, Windows 등 기본적으로 ASCII 호환 문자 집합을 사용하는 모든 곳에서 생성된 입력 파일을 허용합니다. 일부 입력 파일에 알려진 모든 키의 하위 집합만 있으면 혼동될 수 있으며 유사한 상황을 이해하고 유지 관리하고 적응하기가 상대적으로 쉽습니다.


위의 내용을 스크립트로 실행하려면 다음과 같이 저장하세요 paste.awk.

#!/usr/bin/awk -f
BEGIN {
    RS = "(\r\n|\n\r|\r|\n)"
    FS = " *\t *"
    SUBSEP = ":"
}
FNR==1 {
    ++file
}
NF>=2 {
    if ($1 in keynum)
        key = keynum[$1]
    else {
        key = ++keys
        keynum[$1] = key
        keystr[key] = $1
    }
    printf "key = %s, file = %s, value = %s\n", key, file, $2 >/dev/stderr
    value[key,file] = $2
}
END {
    files = file
    for (key = 1; key <= keys; key++) {
        printf "%s", keystr[key]
        for (file = 1; file <= files; file++)
            printf "\t%s", value[key,file]
        printf "\n"
    }
}

input1포함 했다면

        a
A       5
C       4
D       2

그리고 input2포함

        b
A       2
B       5
C       3

그리고 input3포함

        c
B       4
C       4
D       2

그러나 각 줄의 두 번째 문자는 Tab;

printf ' \ta\nA\t5\nC\t4\nD\t2\n' > input1
printf ' \tb\nA\t2\nB\t5\nC\t3\n' > input2
printf ' \tc\nB\t4\nC\t4\nD\t2\n' > input3

또는 위의 텍스트를 파일에 복사하여 붙여넣은 경우 실행 sed -e 's|^\(.\) *|\1\t|' -i input1 input2 input3하여 수정한 다음 실행하세요.

paste.awk input1 input2 input3

산출

        a       b       c
A       5       2       
C       4       3       4
D       2               2
B               5       4

단지 위의 연속된 공백이 실제로는 s라는 것뿐입니다 tab. 보시다시피 웹사이트의 소프트웨어는 탭을 공백으로 변환합니다.

추가하도록 수정됨: 누락된 항목에 대해 사전 정의된 일부 값을 사용하려면 END규칙을 다음과 같이 수정하세요.

END {
    files = file
    for (key = 1; key <= keys; key++) {
        printf "%s", keystr[key]
        for (file = 1; file <= files; file++)
            if ((key SUBSEP file) in value)
                printf "\t%s", value[key,file]
            else
                printf "\t%s", blank
        printf "\n"
    }
}

blank원하는 값을 반영하도록 변수를 설정합니다 . (명령줄을 사용하여 설정하거나 awk 코드를 수정하여 규칙의 어딘가 또는 규칙 시작 부분에 값을 설정할 수 있습니다 ./paste.awk -v blank=0 input1 input2 input3.)BEGINEND

답변3

이것은 GNU awk 버전입니다. 먼저 빈 값을 0으로 채울 수 있도록 모든 키 값을 찾습니다.

keys=$(cut -d $'\t' -f1 file{1,2,3} | sort -u | paste -sd,)
gawk -F'\t' -v keys="$keys" '
    BEGIN {
        n = split(keys,k,/,/)
        for (i=1; i<=n; i++) values[k[i]] = k[i]
    }
    {v[$1] = $2} 
    ENDFILE {
        for (key in values) 
            values[key] = values[key] FS (v[key] ? v[key] : 0)
        delete v
    } 
    END {
        for (key in values) print values[key]
    }
' file1 file2 file3 | sort -t $'\t' -k 1,1

관련 정보