【问题标题】:use awk to identify multi-line record and filtering使用awk识别多行记录和过滤
【发布时间】:2012-06-05 05:14:55
【问题描述】:

我需要处理一个包含多行记录的大数据文件,示例输入:

1  Name      Dan
1  Title     Professor
1  Address   aaa street
1  City      xxx city
1  State     yyy
1  Phone     123-456-7890
2  Name      Luke
2  Title     Professor
2  Address   bbb street
2  City      xxx city
3  Name      Tom
3  Title     Associate Professor
3  Like      Golf
4  Name
4  Title     Trainer
4  Likes     Running

请注意,第一个整数字段是唯一的,并且真正标识了整条记录。所以在上面的输入中我确实有 4 条记录,虽然我不知道每条记录可能有多少行属性。我需要: - 识别有效记录(必须有“名称”和“标题”字段) - 输出每个有效记录的可用属性,比如“姓名”、“标题”、“地址”是需要的字段。

示例输出:

1  Name      Dan
1  Title     Professor
1  Address   aaa street
2  Name      Luke
2  Title     Professor
2  Address   bbb street
3  Name      Tom
3  Title     Associate Professor

所以在输出文件中,记录 4 被删除,因为它没有“名称”字段。记录 3 没有地址字段,但仍被打印到输出,因为它是具有“名称”和“标题”的有效记录。

我可以用 awk 做到这一点吗?但是如何使用每行的第一个“id”字段来识别整条记录呢?

非常感谢 unix shell 脚本专家帮助我! :)

【问题讨论】:

    标签: bash shell awk


    【解决方案1】:

    这似乎有效。有很多方法可以做到这一点,即使在 awk 中也是如此。

    为了便于阅读,我已将其隔开。

    请注意,记录 3 没有显示,因为它缺少您确定为必需的“地址”字段。

    #!/usr/bin/awk -f
    
    BEGIN {
            # Set your required fields here...
            required["Name"]=1;
            required["Title"]=1;
            required["Address"]=1;
    
            # Count the required fields
            for (i in required) enough++;
    }
    
    # Note that this will run on the first record, but only to initialize variables
    $1 != last1 {
            if (hits >= enough) {
                    printf("%s",output);
            }
            last1=$1; output=""; hits=0;
    }
    
    # This appends the current line to a buffer, followed by the record separator (RS)
    { output=output $0 RS }
    
    # Count the required fields; used to determine whether to print the buffer
    required[$2] { hits++ }
    
    END {
            # Print the final buffer, since we only print on the next record
            if (hits >= enough) {
                    printf("%s",output);
            }
    }
    

    【讨论】:

    • 非常感谢您的帮助!我花了一点时间来消化你的解决方案,然后我从中学到了很多东西!!! :) 非常感谢!节省了我很多时间,也从中学到了很多:)
    • 几乎 +1 - 大部分都非常好。您可以删除 NR==1 子句。关于是否需要“地址”,这个问题似乎有点模棱两可。但是,我将其读作 not 是必需的(因此应该输出记录 3)。当我在注释掉“地址”行的情况下运行您的脚本时,我得到了所有四个记录,而不仅仅是 1-3,尽管记录 4 的“名称”为空。原因是您的选择器 @ 987654323@ 仅仅通过被引用就产生了一个与每个字段名称相对应的数组元素并且您没有检查它是否等于1
    • 您可以通过在END 块的末尾添加for (i in required) print i 来验证这一点。您还必须检查 $3 是否为空 (required[$2] == 1 && $3)。额外的条目仍将在数组中创建,但通过显式检查该值不会有任何误报。我认为如果字段丢失或为空,则应排除该记录。 @nanshi 可以验证我的假设是否正确。
    • 主要是优点。 NR==1 块确实是多余的。我把它放进去是为了让下一个块的条件更容易理解。至于“地址”,这就是我在回答中明确指出的原因; OP 说'说“姓名”、“标题”、“地址”是必填字段'。在评论地址行时,我无法复制您的结果。也许您使用的是不常见的 awk 版本?我在 nawk 20110810 (FreeBSD) 和 gawk 4.0.1 上进行了测试。
    • 至于required[$2] 的东西,在END 块中,尝试for (i in required) print i "=" required[i]; 看看会评估什么。存在但为空的 required[$2] 与不存在的评估相同。在 awk 中,0 或 "" 评估为假,而 1 或 /./ 评估为真。这些数组元素是噪音,但不影响脚本的功能。
    【解决方案2】:

    我不擅长 awk,但我会在 Perl 中解决这个问题。这是一个 Perl 解决方案:对于每条记录,它都会记住重要的行以及是否看到了名称和标题。在记录的末尾,如果所有条件都满足,则打印该记录。

    #!/usr/bin/perl
    use warnings;
    use strict;
    
    my ($last, $has_name, $has_title, @record);
    while (<DATA>) {
        my ($id, $key, $value) = split;
        if ($id != $last and @record) {
            print @record if $has_name and $has_title;
            undef @record;
            undef $has_name;
            undef $has_title;
        }
        $has_name  = 1 if $key eq 'Name';
        $has_title = 1 if $key eq 'Title';
        push @record, $_ if grep $key eq $_, qw/Name Address Title/;
        $last = $id;
    }
    
    
    __DATA__
    1  Name      Dan
    1  Title     Professor
    1  Address   aaa street
    1  City      xxx city
    1  State     yyy
    1  Phone     123-456-7890
    2  Name      Luke
    2  Title     Professor
    2  Address   bbb street
    2  City      xxx city
    3  Name      Tom
    3  Title     Associate Professor
    3  Like      Golf
    4  Name
    4  Title     Trainer
    4  Likes     Running
    

    【讨论】:

    • 非常感谢您的 perl 解决方案!我稍后会尝试一下。现在我将使用 awk,因为我仍然需要稍微修改脚本以满足我的下一个需求,这里没有发布。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-04
    • 1970-01-01
    • 1970-01-01
    • 2016-08-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多