【问题标题】:AWK printing fields in multiline records多行记录中的 AWK 打印字段
【发布时间】:2015-06-04 23:44:12
【问题描述】:

我有一个包含多行字段的输入文件。在此文件中,字段模式根据查询大小重复。

ZZZZ
21293

YYYYY     XXX     WWWW   VV
13242     MUTUAL  BOTH   NO

UUUUU   TTTTTTTT  SSSSSSSS   RRRRR   QQQQQQQQ  PPPPPPPP
 3       0                    3       0

NNNNNN  MMMMMMMMM  LLLLLLLLL  KKKKKKKK  JJJJJJJJ
 2       0                     5         3

IIIIII  HHHHHH  GGGGGGG  FFFFFFF  EEEEEEEEEEE  DDDDDDDDDDD
 5       3       0                 3           

我想要的输出是每组字段中的一行。空的 字段应该被标记。示例:“x”

21293 13242 MUTUAL BOTH NO 3 0 X 3 0 X 2 0 X 5 3 5 3 0 X 3 X
12345 67890 MUTUAL BOTH NO 3 0 X 3 0 X 2 0 X 5 3 5 3 0 X 3 X  

我一直在考虑如何使用 awk/unix 脚本获得所需的输出,但无法弄清楚。有任何想法吗?非常感谢!!!

【问题讨论】:

  • 输入中的1234567890在哪里?
  • 那么您在每对第二行中的字段是基于第一行中XXXX 事物的列位置?
  • 不应该XXXXXXXXX 所以它和它下面的值一样长?
  • 没有一种简单的方法可以检测“S”字段在下一行中没有值。从表面上看,“P”字段更容易,至少如果前面的字段都没有丢失的话。 (当然,“L”、“F”和“D”字段也同样有趣。)数据中有空行吗?输入数据未说明从 12345 开始的输出。你怎么知道你什么时候开始一个新的数据块?第二条(数据)线与前一条(掩码)线的对齐程度如何?您确定不能以更易于处理的格式显示数据吗?
  • 为什么输出以5 3 0 X X结尾?不应该是5 3 0 X 3 X吗?

标签: bash unix awk


【解决方案1】:

这不太适合awk 的编程风格,它基于由模式分隔的字段,而不是行上位置可变的字段。但可以做到。

当您处理每一对中的第一行时,扫描它以找到每个字段名称的开头位置。

awk 'NR%3 == 1 {
        delete fieldpos;
        delete fieldlen;
        lastspace = 1;
        fieldindex = 0;
        for (i = 1; i <= length(); i++) {
            if (substr($0, i, 1) != " ") {
                if (lastspace) {
                    fieldpos[fieldindex] = i;
                    if (fieldindex > 0) {
                        fieldlen[fieldindex-1] = i - fieldpos[fieldindex-1];
                    }
                    fieldindex++;
                }
                lastspace = 0;
            } else {
                lastspace = 1;
            }
        }
    }
    NR%3 == 2 {
        for (i = 0; i < fieldindex; i++) {
            if (i in fieldlen) {
                f = substr($0, fieldpos[i], fieldlen[i]);
            } else { # last field, go to end of line
                f = substr($0, fieldpos[i]);
            }
            gsub(/^ +| +$/, "", f); # trim surrounding spaces
            if (f == "") { f = "X" }
            printf("%s ", f);
        }
    }
    NR%15 == 14 { print "" } # print newline after 5 data blocks
'

【讨论】:

  • 再次感谢 Barmar 的回答。还有什么其他编程语言最适合处理这种输入?你会推荐什么?
  • Perl 可能会更好。你想要的主要是一个函数,它允许你在字符串中搜索第一个空格或非空格字符的位置,所以你不必像我一样逐个字符循环。
【解决方案2】:

假设您的字段由空白字符而不是制表符分隔,GNU awk 的 FIELDWITDHS 旨在处理这种情况:

/^ZZZZ/ { if (rec!="") print rec; rec="" }
/^[[:upper:]]/ {
    FIELDWIDTHS = ""
    while ( match($0,/\S+\s*/) ) {
        FIELDWIDTHS = (FIELDWIDTHS ? FIELDWIDTHS " " : "") RLENGTH
        $0 = substr($0,RLENGTH+1)
    }
    next
}
NF {
    for (i=1;i<=NF;i++) {
        gsub(/^\s+|\s+$/,"",$i)
        $i = ($i=="" ? "X" : $i)
    }
    rec = (rec=="" ? "" : rec " ") $0
}
END { print rec }

$ awk -f tst.awk file
2129 13242 MUTUAL BOTH NO 3 0 X 3 0 X 2 0 X 5 3 5 3 0 X 3 X

在其他 awk 中,您将使用 match()/substr()。请注意,上述内容并不完美,因为它会从 21293 中截断一个字符 - 这是因为我不相信您的输入文件是准确的,如果是的话,您还没有告诉我们为什么该数字比前面的字符串长行或如何处理。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-27
    • 1970-01-01
    • 2013-08-29
    • 2020-09-03
    • 2013-03-13
    • 2016-04-05
    相关资源
    最近更新 更多