공통 요소에서 열을 정렬하지만 다른 요소에 자체 행을 제공하는 방법은 무엇입니까?

공통 요소에서 열을 정렬하지만 다른 요소에 자체 행을 제공하는 방법은 무엇입니까?

나는 이것을 사용하여 paste세 개의 텍스트 파일(정렬할 필요가 없음)을 세 개의 열이 있는 하나의 문서로 병합합니다.

paste a.txt b.txt c.txt

나는 열에 공통된 요소가 일치하지 않는 요소(현재는 그렇습니다)와 공유하지 않고 동일한 행을 차지하기를 원합니다. 마찬가지로 고유 요소에는 자체 행이 있어야 합니다. 각 열의 요소는 원래 순서를 유지해야 합니다.

이것은 간단한 예입니다.

입력하다

1 1 1
2 2 2
3 4 4
5 5 5
1 1 2
3 3 3

원하는 출력

1 1 1
2 2 2
3    
  4 4
5 5 5
1 1
    2
3 3 3

다음은 더 복잡한 예입니다.

입력하다

000 000 000
002 002 001
006 006 006
008 008 007
009 009 009
011 012 010
013 013 013
015 015 014
016 016 016
018 019 017
020 020 020
021 021 022
024 024 024
026 025 025
028 026 026
118 028 027
119 118 118
032 119 117
036 032 032
037 033 033
039 034 034
040 037 037
042 039 038
043 040 040
045 042 041
046 043 043
048 045 044
    046 046
    049 047

원하는 출력

000 000 000
        001
002 002
006 006 006
        007
008 008 
009 009 009
        010
011        
    012 
013 013 013
        014
015 015 
016 016 016
        017
018     
    019 
020 020 020
021 021 
        022
024 024 024
    025 025
026 026 026
        027
028 028 
118 118 118
        117
119 119 
032 032 032
    033 033
    034 034
036     
037 037 037
        038
039 039 
040 040 040
        041
042 042 
043 043 043
        044
045 045 
046 046 046
        047
048     
    049

이상적으로는 Linux/Unix에 내장된 도구를 사용하고 싶습니다. 또한 출력을 세 개의 열이 있는 단일 문서로 유지하고 싶습니다(예: > whatever.csv.

내가 얻을 수 있는 가장 가까운 것은 sdiff원본 텍스트 파일에서 실행되는 것이었지만 파일에서 함께 공유된 요소가 올바르게 정렬되었지만 내가 원하는 방식으로 차이점을 처리하지 못했습니다.

답변1

BEGIN {
    # We assume the default input field separator (changeable with "-F")
    # Output will be tab delimited.
    OFS = "\t"
}
{
    # The number of output records that this input record results in.
    k=0

    # "seen" records which new record a field should be part of.
    # There may be NF new records for each input record if all
    # fields are unique.
    delete seen

    # "a" holds all data for the new output records.
    # It's basically a 2-dimensional NFxNF matrix
    # encodod in a 1-dimensional array.
    delete a

    # Iterate over the fields
    for (i=1; i<=NF; ++i) {
        if (!seen[$i]) {
            # This data has not been seen before (in this input record),
            # assign it to the next output line.

            seen[$i] = ++k
        }

        # Assign the input field to the right spot
        a[(seen[$i]-1)*NF + i] = $i
    }

    # Save NF as this is reset by emptying $0 later.
    nf = NF

    # Create and output new lines
    for (j = 1; j<=k; ++j) {
        $0 = ""

        # Create new output record
        for (i = 1; i<=nf; ++i)
            $i = a[(j-1)*nf + i]

        # Output record
        print
    }
}

주어진 데이터에 대해 테스트:

$ awk -f script.awk file
1       1       1
2       2       2
3
        4       4
5       5       5
1       1
                2
3       3       3

다른 데이터에 대한 테스트:

$ cat file
a b c e
1 2 1 1
2 1 1 1
1 1 1 2
$ awk -f script.awk file
a
        b
                c
                        e
1               1       1
        2
2
        1       1       1
1       1       1
                        2

답변2

paste이것은 쉘 스크립트에서 및 를 사용하는 "무차별 대입" 솔루션입니다 read.

#!/bin/sh

paste a.txt b.txt c.txt |
while read -r a b c; do
    if [ "$a" = "$b" ] && [ "$b" = "$c" ]; then
        printf '%s\t%s\t%s\n' "$a" "$b" "$c"
    elif [ "$a" = "$b" ]; then
        printf '%s\t%s\n\t\t%s\n' "$a" "$b" "$c"
    elif [ "$a" = "$c" ]; then
        printf '%s\t\t%s\n\t%s\n' "$a" "$c" "$b"
    elif [ "$b" = "$c" ]; then
        printf '%s\n\t%s\t%s\n' "$a" "$b" "$c"
    else
        printf '%s\n\t%s\n\t\t%s\n' "$a" "$b" "$c"
    fi
done

더 우아한 해결책이 있을 수 있지만 즉시 좋은 해결책이 생각나지 않습니다.

원한다면 이를 사용할 수 있습니다 awk. 결과는 매우 유사해 보일 것입니다. (사용의 한 가지 장점은 유용하다면 작업을 동시에 수행한다는 awk것입니다 .)paste

관련 정보