행과 열 바꾸기


18

아래와 같은 줄이있는 파일이 있습니다.

title1:A1
title2:A2
title3:A3
title4:A4
title5:A5

title1:B1
title2:B2
title3:B3
title4:B4
title5:B5

title1:C1
title2:C2
title3:C3
title4:C4
title5:C5

title1:D1
title2:D2
title3:D3
title4:D4
title5:D5

어떻게하면 되나요?

title1    title2     title3    title4
A1         A2         A3         A4
B1         B2         B3         B4
C1         C2         C3         C4
D1         D2         D3         D4


제발 awk를 사용하지 마십시오, 당신은뿐만 아니라 펄이나 파이썬이나 실제 프로그래밍 언어로 사용자 정의 솔루션을 롤하거나 여러 패스와 함께 tr / cut을 사용하여 원하는 것을 얻을 수 있습니다
Rudolf Olah

답변:



9

명령 줄의 열이있는 행을 바꾸는 사용자 지정 솔루션을 롤링하는 것 외에는 내가 할 수있는 유일한 도구는 ironically이라는 도구 transpose입니다.

설치

불행히도 리포지토리에 없으므로 다운로드하여 컴파일해야합니다. 의존하는 추가 라이브러리가 없기 때문에 이것은 매우 간단합니다. 다음과 같이 달성 할 수 있습니다.

$ gcc transpose.c -o transpose

용법

간단한 텍스트 파일을 쉽게 처리 할 수 ​​있습니다. 예를 들면 다음과 같습니다.

$ cat simple.txt 
X column1 column2 column3
row1 0 1 2
row2 3 4 5
row3 6 7 8
row4 9 10 11

이 명령을 사용하여 바꿀 수 있습니다 :

$ transpose -t --fsep " " simple.txt 
X row1 row2 row3 row4
column1 0 3 6 9
column2 1 4 7 10
column3 2 5 8 11

이 명령은 transpose조옮김 ( -t)이며 사용할 필드 구분 기호는 공백 ( --fsep " ")입니다.

당신의 예

샘플 데이터는 약간 더 복잡한 형식이므로 2 단계로 처리해야합니다. 먼저이를 transpose처리 할 수 있는 형식으로 변환해야합니다 .

이 명령을 실행하면 데이터를보다 가로 형식으로 배치 할 수 있습니다.

$ sed 's/:/ /; /^$/d' sample.txt \
    | sort | paste - - - - -
title1 A1   title1 B1   title1 C1   title1 D1   title2 A2
title2 B2   title2 C2   title2 D2   title3 A3   title3 B3
title3 C3   title3 D3   title4 A4   title4 B4   title4 C4
title4 D4   title5 A5   title5 B5   title5 C5   title5 D5

이제 title1, title2 등의 보조 항목 만 제거하면됩니다.

$ sed 's/:/ /; /^$/d' sample.txt \
    | sort | paste - - - - - | sed 's/\ttitle[0-9] / /g'
title1 A1 B1 C1 D1 A2
title2 B2 C2 D2 A3 B3
title3 C3 D3 A4 B4 C4
title4 D4 A5 B5 C5 D5

이제는 transpose처리 할 수 있는 형식으로되어 있습니다. 다음 명령은 전체 조옮김을 수행합니다.

$ sed 's/:/ /; /^$/d' sample.txt \
    | sort | paste - - - - - | sed 's/\ttitle[0-9] / /g' \
    | transpose -t --fsep " "
title1 title2 title3 title4
A1 B2 C3 D4
B1 C2 D3 A5
C1 D2 A4 B5
D1 A3 B4 C5
A2 B3 C4 D5

8

당신이 사용할 수있는 awk다음 데이터를 처리 paste하고 column포맷 할 수 있습니다.

여기서는 title1게시물의 예일 뿐이며 데이터는 :헤더 + 데이터 사이의 구분 기호를 제외하고는 포함하지 않습니다 .

n인쇄 할 열 수를 나타냅니다 (의 대시와 일치해야 함 paste).

awk -F":" -v n=4 \
'BEGIN { x=1; c=0;} 
 ++c <= n && x == 1 {print $1; buf = buf $2 "\n";
     if(c == n) {x = 2; printf buf} next;}
 !/./{c=0;next}
 c <=n {printf "%s\n", $2}' datafile | \
 paste - - - - | \
 column -t -s "$(printf "\t")"

보다 유연하고 유지 관리하기 쉽도록 스크립트로 작성할 수 있습니다. 다음은 bash 래퍼를 사용하여 awk파이프로 연결 하는 예 column입니다. 이렇게하면 모든 행에서 헤더가 올바른지 확인하는 등 더 많은 데이터 검사를 수행 할 수도 있습니다.

일반적으로 다음과 같이 사용됩니다.

$ ./trans -f data -c 4
title one  title two  title three  title four
A1         A2         A3           A4
B1         B2         B3           B4
C1         C2         C3           C4
D1         D2         D3           D4

헤더는 항상 헤더 폭 저장, 다음도 할 수 짧은 다음 데이터 인 경우 printf때와 %-*s및 건너 뛰기 column모두 함께.

#!/bin/bash

trans()
{
    awk -F":" -v ncol="$1" '
    BEGIN {
        level = 1 # Run-level.
        col   = 1 # Current column.
        short = 0 # If requested to many columns.
    }
    # Save headers and data for row one.
    level == 1 {
        head[col] = $1
        data[col] = $2
        if (++col > ncol) { # We have number of requested columns.
            level = 2
        } else if ($0 == "") { # If request for more columns then available.
            level = 2
            ncol  = col - 2
            short = 1
        } else {
            next
        }
    }
    # Print headers and row one.
    level == 2 {
        for (i = 1; i <= ncol; ++i)
            printf("%s\t", head[i])
        print ""
        for (i = 1; i <= ncol; ++i)
            printf("%s\t", data[i])
        level = 3
        col = ncol + 1
        if (!short)
            next
    }
    # Empty line, new row.
    ! /./ { print ""; col = 1; next }
    # Next cell.
    col > ncol {next}
    {
        printf "%s%s", $2, (col <= ncol) ? "\t" : ""
        ++col
    }
    END {print ""}
    ' "$2"
}

declare -i ncol=4  # Columns defaults to four.
file=""            # Data file (or pipe).

while [[ -n "$1" ]]; do
    case "$1" in
    "-c") ncol="$2"; shift;;
    "-f") file="$2"; shift;;
    *) printf "Usage: %s [-c <columns>] [-f <file> | pipe]\n" \
        "$(basename $0)" >&2;
        exit;;
    esac
    shift
done

trans "$ncol" "$file" | column -t -s "$(printf "\t")"

1
좋은 대답입니다! @JoelDavis와 나는 이것을 해킹 해 왔지만 당신의 대답은 훌륭합니다!
slm

7

파일을 원하는 형식으로 빠르게 넣을 수있는 방법은 다음과 같습니다.

$ grep -Ev "^$|title5" sample.txt | sed 's/title[0-9]://g' | paste - - - -
A1  A2  A3  A4
B1  B2  B3  B4
C1  C2  C3  C4
D1  D2  D3  D4

열 머리글을 원하는 경우 :

$ grep -Ev "^$|title5" sample.txt | sed 's/:.*//' | sort -u | tr '\n' '\t'; \
    echo ""; \
    grep -Ev "^$|title5" a | sed 's/title[0-9]://g' | paste - - - -
title1  title2  title3  title4  
A1      A2      A3      A4
B1      B2      B3      B4
C1      C2      C3      C4
D1      D2      D3      D4

두 번째 명령의 작동 방식

배너 인쇄
grep -Ev "^$|title5" sample.txt | sed 's/:.*//' | sort -u | tr '\n' '\t';
배너를 넣은 후 반납
echo
데이터 행 인쇄
grep -Ev "^$|title5" a | sed 's/title[0-9]://g' | paste - - - -

paste 명령은 단순히 내 작업을 완료했습니다. 답변 주셔서 감사합니다 ...
SK Venkat


3

이것을 공식화하는 더 간결한 방법이 있지만 이것은 일반적인 효과를 얻는 것처럼 보입니다.

[jadavis84@localhost ~]$ sed 's/^title[2-9]://g' file.txt | tr '\n' '\t' | sed 's/title1:/\n/g' ; echo

A1  A2  A3  A4  A5      
B1  B2  B3  B4  B5      
C1  C2  C3  C4  C5      
D1  D2  D3  D4  D5  
[jadavis84@localhost ~]$ 

여러 번의 sed호출이 옳지 않다고 생각합니다 (그리고 sed가 새로운 줄 변환도 할 수 있다고 확신합니다). 아마도 그렇게하는 것이 가장 직접적인 방법은 아닙니다. 또한 이것은 헤더를 제거하지만 행 / 필드의 형식이 올바르게 설정되면 수동으로 생성 할 수 있습니다.

더 나은 대답은 아마도 한 번에 한 가지 일만 수행하도록 사용 sed하거나 awk수행 하기 위해 그 효과를 떨어 뜨릴 것입니다 . 그러나 나는 피곤하기 때문에 이것이 내가 함께 할 수 있었던 것입니다.


Joel-나는 똑같은 실수를하고 그것을 알아 차렸다. 그는 출력에 title5 열을 원하지 않는다.
slm

아, 마지막에 awk를 잘 실행하면 문제가 해결됩니다. 그러나 Sukminder가 완벽한 솔루션을 게시 한 것처럼 보입니다.
Bratchley 2016 년

1

paste아마 당신의 최선의 방법입니다. 당신과 관련 비트를 추출 할 수 있습니다 cut, grep그리고 awk이 같은 :

(awk 'NR==1' RS= infile | cut -d: -f1; cut -sd: -f2 infile)

5 번째 열을 제거해야하는 경우 다음 awk 'NR%5'과 같이 추가 하십시오.

(awk 'NR==1' RS= infile | cut -d: -f1; cut -sd: -f2 infile) | awk 'NR%5'

이제 다음과 paste같이 분류하십시오 .

(awk 'NR==1' RS= infile | cut -d: -f1; cut -sd: -f2 infile) | awk 'NR%5' | paste - - - -

산출:

title1  title2  title3  title4
A1  A2  A3  A4
B1  B2  B3  B4
C1  C2  C3  C4
D1  D2  D3  D4

0

조옮김 부분에 대해서만 최근 비슷한 문제가 발생하여 다음을 사용했습니다.

awk -v fmt='\t%4s'  '{ for(i=1;i<=NF;i++){ a[i]=a[i] sprintf(fmt, $i); } } END { for (i in a) print a[i]; }'

필요에 따라 fmt를 조정하십시오. 각 입력 행에 대해 각 필드를 배열 요소에 연결합니다. awk 문자열 연결은 암시 적입니다. 연산자없이 두 가지를 쓸 때 발생합니다.

샘플 I / O :

i       mark    accep   igna    utaal   bta
-22     -10     -10     -20     -10     -10
-21     -10     -10     -20     -10     -10
-20     -10     -10     -20     -10     -10
-19     -10     0       -10     -10     -10
-18     0       0       -10     0       0
-12     0       0       -10     0       0
-11     0       0       -10     0       0
-10     0       0       -10     0       0

산출:

       i     -22     -21     -20     -19     -18     -12     -11     -10
    mark     -10     -10     -10     -10       0       0       0       0
    accep    -10     -10     -10       0       0       0       0       0
    igna     -20     -20     -20     -10     -10     -10     -10     -10
    utaal    -10     -10     -10     -10       0       0       0       0
     bta     -10     -10     -10     -10       0       0       0       0

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.