Parsing 使用awk sed等人解析没有结束标记的文件中的字段

Parsing 使用awk sed等人解析没有结束标记的文件中的字段,parsing,text,awk,sed,delimited,Parsing,Text,Awk,Sed,Delimited,我想要的输出是一个逗号分隔的文件。如果所有其他方法都失败了,我知道我可以用for/each逻辑或其他东西编写一个脚本,但我希望使用awk和sed可以得到一个优雅的解决方案,这在以前对我很有用。我只是被这个难住了 下面是数据的表示,然后是期望的结果。注意:每个GROUPNUMBER都有一组相关的GROUPMEMBER、GROUPMEMBERID和MEMBERRANK字段,这些字段并不总是像在示例数据中一样位于行的第一个字符处。GROUPNUMBER周围的其他行和文本不重要,也可能有冒号,在这里表示

我想要的输出是一个逗号分隔的文件。如果所有其他方法都失败了,我知道我可以用for/each逻辑或其他东西编写一个脚本,但我希望使用awk和sed可以得到一个优雅的解决方案,这在以前对我很有用。我只是被这个难住了

下面是数据的表示,然后是期望的结果。注意:每个GROUPNUMBER都有一组相关的GROUPMEMBER、GROUPMEMBERID和MEMBERRANK字段,这些字段并不总是像在示例数据中一样位于行的第一个字符处。GROUPNUMBER周围的其他行和文本不重要,也可能有冒号,在这里表示为lorem ipsum文本。有些GroupNumber根本没有相关字段,如最后一行中Group888的示例数据所示。此外,在示例数据中,GROUPNUMBER部分显示为用空行分隔,但情况并非总是如此,有时下一个部分与上一个部分之间没有换行符

样本数据:

loremipsum: loremipsum?# loremipsum/123: loremipsumxx GROUPNUMBER:111222
loremipsum123:loremispum loremipsumxxxx
GROUPMEMBER:Joe:MEMBERRANK:1
GROUPMEMBERID:1234
GROUPMEMBER:Mike:MEMBERRANK:1
GROUPMEMBERID:2234
loremipsum14e3:loremispum loremipsumxxxx
loremipsum1eer534:loremispum loremipsumxxfgt
GROUPMEMBER:Sue:MEMBERRANK:89
GROUPMEMBERID:3234
GROUPMEMBER:John:MEMBERRANK:323
GROUPMEMBERID:4234:loremipsumaaa_loremipsum

loremipsum: loremipsum..<?# loremipsum/123: loremipsumxx GROUPNUMBER:333444
loremipsum123:loremispum loremipsumxxxx
GROUPMEMBER:Frank:MEMBERRANK:4
GROUPMEMBERID:5234
GROUPMEMBER:Laurie:MEMBERRANK:4
GROUPMEMBERID:6234

loremipsum: loremipsum..<?# loremipsum/123: loremipsumxx GROUPNUMBER:88888
loremipsum123:loremispum loremipsumxxxx

在GNU中可能几乎不可能。。。但最好使用哈希表,或者至少使用比保留空间更多的变量:

sed-nE'/GROUPNUMBER://{s/*://;h}/GROUPMEMBER://{N;G;s/GROUPMEMBER:.*:MEMBERRANK:..\N GroupMemberId:[^:::*.\N./\4、\1、\2、\3/p}${g;s/$/,,/p}文件

这是对最后一组的一种黑客处理。。。最后一行始终是最终的组号和三个逗号

要点:

sed-nE仅在指定时打印,并且不允许反斜杠捕获组 h来保存组号 NG在GROUPMEMBER行上添加后续行和groupnumber 捕获组1-4重新排序以格式化输出 最后一行$上的hack,它使用g获取最后一个GROUPNUMBER并用三个逗号打印出来
在GNU中可能几乎不可能。。。但最好使用哈希表,或者至少使用比保留空间更多的变量:

sed-nE'/GROUPNUMBER://{s/*://;h}/GROUPMEMBER://{N;G;s/GROUPMEMBER:.*:MEMBERRANK:..\N GroupMemberId:[^:::*.\N./\4、\1、\2、\3/p}${g;s/$/,,/p}文件

这是对最后一组的一种黑客处理。。。最后一行始终是最终的组号和三个逗号

要点:

sed-nE仅在指定时打印,并且不允许反斜杠捕获组 h来保存组号 NG在GROUPMEMBER行上添加后续行和groupnumber 捕获组1-4重新排序以格式化输出 最后一行$上的hack,它使用g获取最后一个GROUPNUMBER并用三个逗号打印出来
这不是特别容易,但也不是难以置信的困难。所有有趣的信息都在由冒号分隔的字段中,因此其中一部分是让awk根据冒号将输入行拆分为字段-F:。然后需要识别组号、组成员、成员等级和成员ID。任何没有匹配信息的行都将被忽略。查找字段值可以通过扫描字段来完成,查找与关键字匹配的字段,并将其后面的字段作为值返回。在下面的代码中,函数提取器完成了这项工作。还需要记录一个组号打印了多少次。在输入结束时,或在识别新组号时,如果旧组号已打印零次,则需要打印组号信息。函数print_member打印一个成员;这样可以节省3次写出printf语句的时间

awk -F: '
function extractor(tag,   i)
{
    for (i = 1; i < NF; i++)
        if ($i ~ tag)
            return $(i + 1)
    return ""
}
function print_member()
{
    printf "%s,%s,%s,%s\n", groupnumber, groupmember, groupmemberid, memberrank
}
    /GROUPNUMBER:[0-9]+/ {
        if (groupnumber != "" && groupcount == 0)
            print_member()
        groupnumber = extractor("GROUPNUMBER")
        groupmember = ""
        memberrank = ""
        groupmemberid = ""
        groupcount = 0
    }
    /GROUPMEMBER:[^:]+:MEMBERRANK:[0-9]+/ {
        groupmember = extractor("GROUPMEMBER")
        memberrank = extractor("MEMBERRANK")
    }
    /GROUPMEMBERID:[0-9]+/ {
        groupmemberid = extractor("GROUPMEMBERID")
        print_member()
        groupcount++
    }
    END {
        if (groupcount == 0)
            print_member()
    }' data
这似乎是必需的输出。现在考虑一个改变了的输入文件,增加了很多这样的内容:

loremipsum: loremipsum?# loremipsum/123: loremipsumxx GROUPNUMBER:111222:hydrangea
loremipsum123:loremispum loremipsumxxxx
GROUPMEMBER:Joe:MEMBERRANK:1:orchid
GROUPMEMBERID:1234
GROUPMEMBER:Mike:piscatore:MEMBERRANK:1
GROUPMEMBERID:2234
loremipsum14e3:loremispum loremipsumxxxx
loremipsum1eer534:loremispum loremipsumxxfgt
GROUPMEMBER:Sue:MEMBERRANK:89
GROUPMEMBERID:3234
GROUPMEMBER:John:MEMBERRANK:323
GROUPMEMBERID:4234:loremipsumaaa_loremipsum
loremipsum: loremipsum..<?# loremipsum/123: loremipsumxx GROUPNUMBER:333444
loremipsum123:loremispum loremipsumxxxx
GROUPMEMBER:Frank:MEMBERRANK:4
GROUPMEMBERID:5234
GROUPMEMBER:Laurie:MEMBERRANK:4
GROUPMEMBERID:6234
loremipsum: loremipsum..<?# loremipsum/123: loremipsumxx GROUPNUMBER:88888
loremipsum123:loremispum loremipsumxxxx
loremipsum: loremipsum..<?# loremipsum/123: loremipsumxx GROUPNUMBER:222444
loremipsum123:loremispum loremipsumxxxx
GROUPMEMBER:Helen Mary Ann:MEMBERRANK:1
loremipsum: loremipsum..<?# loremipsum/123: loremipsumxx GROUPNUMBER:222555
loremipsum123:loremispum loremipsumxxxx
loremipsum123:loremispum loremipsumxxxx
这看起来很合理。海伦·玛丽·安名字中的空格并不重要;她没有会员身份证。中间没有信息的组也显示正确。

显然,您可以将其转换为可用的shell脚本,方法是将其放入文件中并用$@替换数据,这样它将处理命令行中给定的文件名,或者在没有此类名称时读取标准输入

如注释中所述,上述代码假设GROUPMEMBER和MEMBERRANK字段在一行上是连续的,中间没有任何随机的“ipsum lorem”类型字段。如果事实上,在我在第二个数据集中使用piscatore时,中间可能有一个“ipsum lorem”字段,那么需要修改脚本以分别识别GROUPMEMBER和MEMBERRANK。这还有一个额外的优点,即如果输入包含:

ipsum lorem:MEMBERRANK:1:ipsum lorem:GROUPMEMBER:Hailey:ipsum lorem

然后,在第二个示例中,它将以任意顺序使用行准确识别信息。GROUPMEMBERID必须是具有给定GROUPNUMBER的每个成员的三个条目中的最后一个

awk -F: '
function extractor(tag,   i)
{
    for (i = 1; i < NF; i++)
        if ($i ~ tag)
            return $(i + 1)
    return ""
}
function print_member()
{
    printf "%s,%s,%s,%s\n", groupnumber, groupmember, groupmemberid, memberrank
}
    /GROUPNUMBER:[0-9]+/ {
        if (groupnumber != "" && groupcount == 0)
            print_member()
        groupnumber = extractor("GROUPNUMBER")
        groupmember = ""
        memberrank = ""
        groupmemberid = ""
        groupcount = 0
    }
    /GROUPMEMBER:[^:]+/ {
        groupmember = extractor("GROUPMEMBER")
    }
    /MEMBERRANK:[0-9]+/ {
        memberrank = extractor("MEMBERRANK")
    }
    /GROUPMEMBERID:[0-9]+/ {
        groupmemberid = extractor("GROUPMEMBERID")
        print_member()
        groupcount++
    }
    END {
        if (groupcount == 0)
            print_member()
    }' data

这不是特别容易,但也不是难以置信的困难。所有有趣的信息都在由冒号分隔的字段中,因此其中一部分是让awk拆分输入行 输入基于冒号的字段-F:。然后需要识别组号、组成员、成员等级和成员ID。任何没有匹配信息的行都将被忽略。查找字段值可以通过扫描字段来完成,查找与关键字匹配的字段,并将其后面的字段作为值返回。在下面的代码中,函数提取器完成了这项工作。还需要记录一个组号打印了多少次。在输入结束时,或在识别新组号时,如果旧组号已打印零次,则需要打印组号信息。函数print_member打印一个成员;这样可以节省3次写出printf语句的时间

awk -F: '
function extractor(tag,   i)
{
    for (i = 1; i < NF; i++)
        if ($i ~ tag)
            return $(i + 1)
    return ""
}
function print_member()
{
    printf "%s,%s,%s,%s\n", groupnumber, groupmember, groupmemberid, memberrank
}
    /GROUPNUMBER:[0-9]+/ {
        if (groupnumber != "" && groupcount == 0)
            print_member()
        groupnumber = extractor("GROUPNUMBER")
        groupmember = ""
        memberrank = ""
        groupmemberid = ""
        groupcount = 0
    }
    /GROUPMEMBER:[^:]+:MEMBERRANK:[0-9]+/ {
        groupmember = extractor("GROUPMEMBER")
        memberrank = extractor("MEMBERRANK")
    }
    /GROUPMEMBERID:[0-9]+/ {
        groupmemberid = extractor("GROUPMEMBERID")
        print_member()
        groupcount++
    }
    END {
        if (groupcount == 0)
            print_member()
    }' data
这似乎是必需的输出。现在考虑一个改变了的输入文件,增加了很多这样的内容:

loremipsum: loremipsum?# loremipsum/123: loremipsumxx GROUPNUMBER:111222:hydrangea
loremipsum123:loremispum loremipsumxxxx
GROUPMEMBER:Joe:MEMBERRANK:1:orchid
GROUPMEMBERID:1234
GROUPMEMBER:Mike:piscatore:MEMBERRANK:1
GROUPMEMBERID:2234
loremipsum14e3:loremispum loremipsumxxxx
loremipsum1eer534:loremispum loremipsumxxfgt
GROUPMEMBER:Sue:MEMBERRANK:89
GROUPMEMBERID:3234
GROUPMEMBER:John:MEMBERRANK:323
GROUPMEMBERID:4234:loremipsumaaa_loremipsum
loremipsum: loremipsum..<?# loremipsum/123: loremipsumxx GROUPNUMBER:333444
loremipsum123:loremispum loremipsumxxxx
GROUPMEMBER:Frank:MEMBERRANK:4
GROUPMEMBERID:5234
GROUPMEMBER:Laurie:MEMBERRANK:4
GROUPMEMBERID:6234
loremipsum: loremipsum..<?# loremipsum/123: loremipsumxx GROUPNUMBER:88888
loremipsum123:loremispum loremipsumxxxx
loremipsum: loremipsum..<?# loremipsum/123: loremipsumxx GROUPNUMBER:222444
loremipsum123:loremispum loremipsumxxxx
GROUPMEMBER:Helen Mary Ann:MEMBERRANK:1
loremipsum: loremipsum..<?# loremipsum/123: loremipsumxx GROUPNUMBER:222555
loremipsum123:loremispum loremipsumxxxx
loremipsum123:loremispum loremipsumxxxx
这看起来很合理。海伦·玛丽·安名字中的空格并不重要;她没有会员身份证。中间没有信息的组也显示正确。

显然,您可以将其转换为可用的shell脚本,方法是将其放入文件中并用$@替换数据,这样它将处理命令行中给定的文件名,或者在没有此类名称时读取标准输入

如注释中所述,上述代码假设GROUPMEMBER和MEMBERRANK字段在一行上是连续的,中间没有任何随机的“ipsum lorem”类型字段。如果事实上,在我在第二个数据集中使用piscatore时,中间可能有一个“ipsum lorem”字段,那么需要修改脚本以分别识别GROUPMEMBER和MEMBERRANK。这还有一个额外的优点,即如果输入包含:

ipsum lorem:MEMBERRANK:1:ipsum lorem:GROUPMEMBER:Hailey:ipsum lorem

然后,在第二个示例中,它将以任意顺序使用行准确识别信息。GROUPMEMBERID必须是具有给定GROUPNUMBER的每个成员的三个条目中的最后一个

awk -F: '
function extractor(tag,   i)
{
    for (i = 1; i < NF; i++)
        if ($i ~ tag)
            return $(i + 1)
    return ""
}
function print_member()
{
    printf "%s,%s,%s,%s\n", groupnumber, groupmember, groupmemberid, memberrank
}
    /GROUPNUMBER:[0-9]+/ {
        if (groupnumber != "" && groupcount == 0)
            print_member()
        groupnumber = extractor("GROUPNUMBER")
        groupmember = ""
        memberrank = ""
        groupmemberid = ""
        groupcount = 0
    }
    /GROUPMEMBER:[^:]+/ {
        groupmember = extractor("GROUPMEMBER")
    }
    /MEMBERRANK:[0-9]+/ {
        memberrank = extractor("MEMBERRANK")
    }
    /GROUPMEMBERID:[0-9]+/ {
        groupmemberid = extractor("GROUPMEMBERID")
        print_member()
        groupcount++
    }
    END {
        if (groupcount == 0)
            print_member()
    }' data

在这个论坛上,我曾多次使用这些方法,我们鼓励其他人发布他们尝试过的东西。请将你尝试过的东西贴出来,并说明是什么阻止了你的写作。你的问题到底是什么?虽然我相信它可以在sed中完成,但是awk解决方案将更容易、更可读。您的问题是解释如何识别GROUPNUMBER:111222等。您输入的所有其他文本中的字符串都是大写,然后是冒号,然后是数字。这在本论坛上对我很有用,我们鼓励其他人发布他们尝试过的内容。请将你尝试过的东西贴出来,并说明是什么阻止了你的写作。你的问题到底是什么?虽然我相信它可以在sed中完成,但是awk解决方案将更容易、更可读。您的问题是解释如何识别GROUPNUMBER:111222等。您输入的所有其他文本中的字符串都是大写的,然后是冒号,然后是数字?如果GROUPNUMBER跟在GROUPMEMBER后面,则会中断,单个服务器上的GROUPMEMBERID或MEMBERRANKline@jhnc-如果示例中的数据不能合理地充分说明可能发生的情况,则是的,处理可能需要更改。示例数据清楚地显示,GROUPNUMBER只显示在一行上,没有任何其他项。如果在完整的数据集上不准确,则需要在问题中显示。我们只需要回答问题所显示的内容,或者可以推断出的内容。问题的最后一行提到,接下来的章节可能没有任何内容linebreak@jhnc:由于示例数据中的序言说明,GROUPNUMBER部分显示为用空行分隔,但情况并非总是如此,我认为这意味着空白行又名“断线”并不总是存在,如我的第二个数据文件中所示。如果“换行符”真的是“换行符”——一种可能但不一定合理的解释——那么是的,需要做更多的工作。YMMV。除非OP建议换行符是“换行符”而不是“空行”,否则我不会担心。很难解释清楚这一点,抱歉造成混淆。每行后面都有一个换行符。我是说额外的空行会增加换行?从样本中看到的数据并不总是存在,有时看起来像乔纳森的数据。我提到这一点是因为我不能依赖双换行符来表示组的结束。使用函数有点像编写脚本,但将它们放在单个awk命令中太酷了。迫不及待地想明天在工作中试试这个。但有一件事,最终输出的第二行显示了Joeb,但应该是Mike,这是一个错误类型吗?如果GROUPNUMBER在单个line@jhnc-如果示例中的数据不能合理地充分说明可能发生的情况,则是的,处理
可能需要改变。示例数据清楚地显示,GROUPNUMBER只显示在一行上,没有任何其他项。如果在完整的数据集上不准确,则需要在问题中显示。我们只需要回答问题所显示的内容,或者可以推断出的内容。问题的最后一行提到,接下来的章节可能没有任何内容linebreak@jhnc:由于示例数据中的序言说明,GROUPNUMBER部分显示为用空行分隔,但情况并非总是如此,我认为这意味着空白行又名“断线”并不总是存在,如我的第二个数据文件中所示。如果“换行符”真的是“换行符”——一种可能但不一定合理的解释——那么是的,需要做更多的工作。YMMV。除非OP建议换行符是“换行符”而不是“空行”,否则我不会担心。很难解释清楚这一点,抱歉造成混淆。每行后面都有一个换行符。我是说额外的空行会增加换行?从样本中看到的数据并不总是存在,有时看起来像乔纳森的数据。我提到这一点是因为我不能依赖双换行符来表示组的结束。使用函数有点像编写脚本,但将它们放在单个awk命令中太酷了。迫不及待地想明天在工作中试试这个。但有一件事,最终输出的第二行显示Joeb,但应该是Mike,这是打字错误吗?
111222,Joe,1234,1
111222,Mike,2234,1
111222,Sue,3234,89
111222,John,4234,323
333444,Frank,5234,4
333444,Laurie,6234,4
88888,,,
222444,Helen Mary Ann,,1
222555,,,