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);

その後、空行を省略し、1 つの QSUM 入力と次の QSUM 入力の間の入力行セットを収集する関数を作成します。大文字と空白のみを含む行はヘッダー、他のコンテンツを含む行はデータであり、フィールドは前のヘッダー行に合わせてソートされます。 (入力と出力の一番上の行が誤ってソートされているのが誤字であると仮定します。)

各グループについて、ヘッダーを列名と一致させて、データ項目が属するハトの集まりを決定します(当然、defColと同じ配列へのインデックスとして)。その後、defColインデックスを使用してフィールドを並べ替え、printfフォーマッタのdefCol幅を使用して詳細行を印刷できます。

複雑に見えますが、柔軟で(近い将来、他の同様の問題に直面するようです)、体系的です。以前に見たことのないヘッダー、レポートに長すぎるフィールドなどを表示することもできます。

より詳細な機能説明:

まず、入力グループが完了するタイミングを検出する条件が必要です。これは「制御中断」でした。これは「QSUM HEADER」または同様の内容を含む行で発生し、いくつかのパターンを使用して異なる間隔を使用できます。また、(まだグループがないため)このタスクが最初に発生したくないため、END条件でも発生する必要があります(最後のグループにはトリガーするタイトルがないため)。 )。

入力の残りの部分を使用して、すべてのヘッダー部分を1つの長い文字列に追加し、すべてのデータ部分を別の長い文字列に追加し、すべての空行を無視できます。

グループを出力するには、ヘッダー文字列を介してデータヘッダーの位置を見つけ、match()およびsubstr()関数を使用してデータ文字列内のその場所のフィールドを選択します。ヘッダ項目と同じインデックスを使用して、各データ項目を配列に格納できます。

その後、タイトル固定テキストを出力するのと同じように、データフィールドを出力できます。

これらすべてが混乱して聞こえますが、実際には非常に簡単です。今はコーディングできませんが、今日後でフレームワークを公開できます。

実際、入力(公開)は、間隔の変化のために安定して解析されない可能性があります。

たとえば、STOCK、DATE、TIMEの下の値が一致しません。 HEADERにはスペースがあるため、フィールド数を数えることはできません。複数のスペースを区切り文字として使用するか、「十分に近い」フィールドソートを使用します。これは40行の例です。他のリスクもあります。

関連情報