awk liner 将输入数据格式化为所需的输出

awk liner 将输入数据格式化为所需的输出

这是输入文件。


    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 liner


    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

使用 GNU awk for FPATand FIELDWIDTHS(然后利用 gawks 的\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

作为固定宽度字段,即使任何字段为空。

我不会评论它或以其他方式一般性地解释它,因为恕我直言,代码非常清楚,你问了很多文本操作问题,所以是时候通过阅读一些代码来了解 awk 了,查找一下手册页,print在您需要查看变量具有哪些值的位置添加语句等。如果您有具体问题,那么当然请随时询问。

上述操作针对此输入文件运行,该文件与您的问题中的文件相同,但 QSUM、STOCK、DATA 和 TIME 标头的不一致对齐问题已得到修复,因为我不相信您的实际输入会那么混乱(但如果是的话,处理起来会很容易):

$ 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 行示例:还有其他危险。

相关内容