awk 라이너는 입력 데이터를 원하는 출력으로 형식화합니다.

awk 라이너는 입력 데이터를 원하는 출력으로 형식화합니다.

입력 파일입니다.


    QSUM HEADER                          STOCK   DATE    TIME
206  CC-USER REJECT SENT             TNPPP   200322  104914600

TI         JPS        TNN        LTNN       PP         JP
                      8          6          0          1

AA         NS                                          CPOODE
4          899599991119

TYPE       AI
12         18

QSUM HEADER                          STOCK   DATE    TIME
206  CC-USER REJECT SENT             TNPPP   200322  115844000

TI         JPS        TNN        LTNN       PP         JP
                      8          6          0          1

AA         NS                                          CPOODE
4          899599991555

TYPE       AI
12         18
QSUM HEADER                          STOCK   DATE    TIME
103  SUITE FAIL, SUBTRACT FAILURE   TPNRM   200318  031124100

TI         PNC        TNN        PP         JP         AA
2          1499       177        123        1          4

NS                                          FLAG       
999999999999                                ORIGIN

TI         CPO


QSUM HEADER                          STOCK   DATE    TIME
103  SUITE FAIL, SUBTRACT FAILURE   TPNRM   200318  031124200

TI         PNC        TNN        PP         JP         AA
2          1499       177        123        1          4

NS                                          FLAG       
999999999999                                ORIGIN

TI         CPO

다음 출력이 필요합니다


    QSUM HEADER                          STOCK   DATE    TIME       TI     PNC       JPS        TNN        LTNN       PP         JP AA         NS                     FLAG                     OPCODE TYPE       AI TI CPO
206  CC-USER REJECT SENT             TNPPP   200322  104914600                               8          6          0          1 4          899599991119                                       12         18
206  CC-USER REJECT SENT             TNPPP   200322  115844000                               8          6          0          1 4          899599991555                                       12         18
103  SUITE FAIL, SUBTRACT FAILURE   TPNRM   200318  031124100    2     1499                 177                   123         1 4          999999999999          ORIGIN
103  SUITE FAIL, SUBTRACT FAILURE   TPNRM   200318  031124200    2     1499                 177                   123         1 4          999999999999          ORIGIN

나는 다음 샘플 awk 라이너를 시도했습니다


    awk 'BEGIN{print ("QSUM HEADER                          STOCK   DATE    TIME       TI     PNC       JPS        TNN        LTNN       PP         JP AA         NS                     FLAG                     OPCODE TYPE       AI TI CPO")}

/^[0-9]/{print a["o"] " " a["p"] " " a["q"]
              ;delete a}

             /^[0-9]/{a["o"]=$0
              next}
             /TNN/{getline
                    a["p"]=$0
                      next}
              /NS/{getline
                   a["q"]=$0
                   next}
                   END
                {print a["o"] " " a["p"] " " a["q"];}'

각 헤더 유형에 대해 패턴을 일치시킨 후 다음 줄을 배열해야 합니다. 이 결과를 달성하기 위한 다른 좋은 대안. 이 코드는 제게는 잘 맞지만 여기서 문제는 헤더만큼 코드가 점점 길어진다는 것입니다.

답변1

FPATand 에는 GNU awk를 사용합니다 (그런 다음 FIELDWIDTHSgawks의 \s/\S약어를 사용합니다 [[:space:]]/[^[:space:]]):

$ cat tst.awk
BEGIN { OFS="\t" }

/^\s*$/ { next }        # skip blank lines

/^\s*QSUM\s/ {          # start of a new record
    numRecs++
    lineNr = 0
}

{
    if ( (++lineNr) % 2 == 1 ) {
        # tags line so find every tag and field width
        FPAT = "\\S+\\s*"
        $0 = $0
        for (i=1; i<=NF; i++) {
            tag = $i
            fw = (i>1 ? fw " " : "") (i<NF ? length(tag) : "*")
            gsub(/^\s+|\s+$/,"",tag)
            if ( !(tag in tagNames2nrs) ) {
                tagNrs2names[++numTags] = tag
                tagNames2nrs[tag] = numTags
            }
            fldNr2tagNr[i] = tagNames2nrs[tag]
        }
        FPAT = ""
    }
    else {
        # vals line so use the field widths found for tags
        FIELDWIDTHS = fw
        $0 = $0
        for (i=1; i<=NF; i++) {
            val = $i
            gsub(/^\s+|\s+$/,"",val)
            tagNr = fldNr2tagNr[i]
            vals[numRecs,tagNr] = val
        }
        FIELDWIDTHS = ""
    }
}

END {
    for (tagNr=1; tagNr<=numTags; tagNr++) {
        tag = tagNrs2names[tagNr]
        printf "%s%s", tag, (tagNr<numTags ? OFS : ORS)
    }

    for (recNr=1; recNr<=numRecs; recNr++) {
        for (tagNr=1; tagNr<=numTags; tagNr++) {
            val = vals[recNr,tagNr]
            printf "%s%s", val, (tagNr<numTags ? OFS : ORS)
        }
    }
}

.

$ awk -f tst.awk file | column -s$'\t' -t
QSUM  HEADER                        STOCK  DATE    TIME       TI  JPS  TNN  LTNN  PP   JP  AA  NS            CPOODE  TYPE  AI  PNC   FLAG    CPO
206   CC-USER REJECT SENT           TNPPP  200322  104914600           8    6     0    1   4   899599991119          12    18
206   CC-USER REJECT SENT           TNPPP  200322  115844000           8    6     0    1   4   899599991555          12    18
103   SUITE FAIL, SUBTRACT FAILURE  TPNRM  200318  031124100  2        177        123  1   4   999999999999                    1499  ORIGIN
103   SUITE FAIL, SUBTRACT FAILURE  TPNRM  200318  031124200  2        177        123  1   4   999999999999                    1499  ORIGIN

각 행 쌍에는 서로 다른 필드가 있고 그 중 일부는 비어 있으므로 데이터를 읽으려면 고정 필드에 의존해야 하지만, 읽으려고 시도하기 전에는 필드 수와 필드 너비를 알 수 없습니다. 따라서 이 스크립트는 FPAT를 사용하여 각 태그 줄에서 모든 태그(이름, 제목, 제목)를 찾습니다.

TI         JPS        TNN        LTNN       PP         JP

각 필드의 너비(태그 이름 및 다음 공백 포함)를 결정한 다음 FIELDWIDTHS를 사용하여 후속 값 행에서 값을 읽을 수 있습니다.

                      8          6          0          1

고정 너비 필드이므로 필드가 비어 있는 경우에도 마찬가지입니다.

나는 그것에 대해 언급하거나 일반적으로 설명하지 않을 것입니다. IMHO 코드는 매우 명확하고 텍스트 조작에 대한 많은 질문을했기 때문에 이제 일부 코드를 읽고 awk에 대해 배울 시간입니다. 매뉴얼 페이지를 찾아보십시오 print. 변수가 어떤 값을 가지고 있는지 확인해야 하는 곳에 명령문 등을 추가해야 합니다. 구체적인 질문이 있으시면 언제든지 문의해 주시기 바랍니다.

위의 내용은 귀하의 질문과 동일한 파일인 이 입력 파일에 대해 실행되었지만 QSUM, STOCK, DATA 및 TIME 헤더의 일관되지 않은 정렬이 수정되었습니다. 귀하의 실제 입력이 그렇게 엉망일 것이라고 생각하지 않기 때문입니다. up (그러나 그렇다면 처리하기 쉬울 것입니다):

$ cat file

QSUM HEADER                          STOCK   DATE    TIME
206  CC-USER REJECT SENT             TNPPP   200322  104914600

TI         JPS        TNN        LTNN       PP         JP
                      8          6          0          1

AA         NS                                          CPOODE
4          899599991119

TYPE       AI
12         18

QSUM HEADER                          STOCK   DATE    TIME
206  CC-USER REJECT SENT             TNPPP   200322  115844000

TI         JPS        TNN        LTNN       PP         JP
                      8          6          0          1

AA         NS                                          CPOODE
4          899599991555

TYPE       AI
12         18
QSUM HEADER                         STOCK   DATE    TIME
103  SUITE FAIL, SUBTRACT FAILURE   TPNRM   200318  031124100

TI         PNC        TNN        PP         JP         AA
2          1499       177        123        1          4

NS                                          FLAG
999999999999                                ORIGIN

TI         CPO


QSUM HEADER                         STOCK   DATE    TIME
103  SUITE FAIL, SUBTRACT FAILURE   TPNRM   200318  031124200

TI         PNC        TNN        PP         JP         AA
2          1499       177        123        1          4

NS                                          FLAG
999999999999                                ORIGIN

TI         CPO

답변2

저는 이 문제를 한번에 해결하고 싶습니다. BEGIN 섹션부터 시작하여 원하는 배열 열을 포함하는 배열을 만듭니다.산출, 을 위한. 열 너비 및 텍스트(첫 번째):

BEGIN {
    defCol[ 1] = "  5 QSUM";
    defCol[ 2] = " 28 HEADER";
    ...
    defCol[19] = "  6 CPO";
}

제목만 인쇄하는 함수를 작성하여 이를 확인하세요. 다음과 같이 defCol을 반복할 수 있습니다.

printf ("%.*s", 0 + defCol[j], substr (defCol[j], 5);

그런 다음 빈 줄을 생략하고 하나의 QSUM 입력과 다음 QSUM 입력 사이의 입력 줄 집합을 수집하는 함수를 작성합니다. 대문자와 공백만 포함된 행은 머리글이고, 다른 콘텐츠가 포함된 행은 데이터이며, 필드는 이전 머리글 행에 맞춰 정렬됩니다. (입력 및 출력의 맨 위 행이 잘못 정렬된 것이 오타라고 가정합니다.)

각 그룹에 대해 헤더를 열 이름과 일치시켜 데이터 항목이 속한 비둘기집을 결정합니다(당연히 defCol과 동일한 배열에 대한 인덱스로). 그런 다음 defCol 인덱스를 사용하여 필드를 정렬하고 printf 포맷터의 defCol 너비를 사용하여 세부 행을 인쇄할 수 있습니다.

복잡해 보이지만 유연하고(가까운 미래에 다른 유사한 문제에 직면하게 될 것 같습니다) 체계적입니다. 이전에 본 적이 없는 헤더, 보고서에 너무 긴 필드 등을 표시할 수도 있습니다.

더 자세한 기능 설명:

먼저, 입력 그룹이 완료되는 시기를 감지하는 조건이 필요합니다. 이는 "제어 중단"이었습니다. 이는 "QSUM HEADER" 또는 유사한 내용이 포함된 줄에서 발생하며 일부 패턴을 사용하여 다른 간격을 허용합니다. 또한 (아직 그룹이 없기 때문에) 이 작업이 처음으로 발생하는 것을 원하지 않으며 END 조건에서도 발생해야 합니다(마지막 그룹에는 이를 트리거할 제목이 없기 때문에). ).

입력의 나머지 부분을 사용하여 모든 헤더 부분을 하나의 긴 문자열에 추가하고 모든 데이터 부분을 다른 긴 문자열에 추가하고 모든 빈 줄을 무시할 수 있습니다.

그룹을 출력하려면 헤더 문자열을 통해 데이터 헤더의 위치를 ​​찾고 match() 및 substr() 함수를 사용하여 데이터 문자열에서 해당 위치의 필드를 선택하면 됩니다. 헤더 항목과 동일한 인덱스를 사용하여 각 데이터 항목을 배열에 저장할 수 있습니다.

그런 다음 제목 고정 텍스트를 출력하는 것처럼 데이터 필드를 출력할 수 있습니다.

이 모든 것이 혼란스럽게 들리지만 실제로는 매우 간단합니다. 지금은 코딩할 수 없지만 오늘 나중에 프레임워크를 게시할 수 있을 것입니다.

실제로 입력(게시됨)은 간격의 변화로 인해 안정적으로 구문 분석되지 않을 수 있습니다.

예를 들어 STOCK, DATE, TIME 아래의 값이 일치하지 않습니다. HEADER에는 공백이 있으므로 필드 수를 셀 수 없습니다. 두 개 이상의 공백을 구분 기호로 사용하거나 "충분히 가까운" 필드 정렬을 사용합니다. 이것은 단지 40줄의 예일 뿐입니다. 다른 위험도 있습니다.

관련 정보