Bash脚本组和特定字段的计数

时间:2015-01-17 17:06:53

标签: bash

抱歉,如果我打开一个新问题,但它与之前的问题无关,因为现在我需要一个bash命令来分析输出。

我有一个查询输出存储在这样的文件中:

3277654321    333011123456789
3277654321    333015123456789
3277654321    333103123456789
3277654321    333201123456789
3291234567    333991123456789
3291234567    333991123456789
3291234567    333011123456789

我需要的是一个bash命令来计算具有相同前5位数的field1和field2并报告这样的输出:

3277654321=4;33301=2;33310=1;33320=1    
3291234567=3;33399=2;33301=1

由于 卢卡斯。

2 个答案:

答案 0 :(得分:1)

在原始数据

上使用awk

您正在寻求的是一个控制中断报告。一次,维基百科的条目对这个问题没什么帮助。样本数据按顺序显示;因此,该解决方案假定数据已排序(但如果未排序,则在awk脚本之前添加排序操作是微不足道的; OTOH,因为数据来自数据库,所以DBMS可以很好地完成对数据进行排序。)

出于测试目的,我创建了一个包含以下内容的文件awk.script

{   f1 = $1
    f2 = substr($2, 1, 5)
    if (oldf1 != f1)
    {
        if (oldf1 != 0)
        {
            summary = summary ";" oldf2 "=" f2_count
            printf("%s=%d%s\n", oldf1, f1_count, summary)
        }
        oldf1 = f1
        f1_count = 0
        oldf2 = f2
        f2_count = 0
        summary = ""
    }
    else if (oldf2 != f2)
    {
        summary = summary ";" oldf2 "=" f2_count
        oldf2 = f2
        f2_count = 0
    }
    f1_count++
    f2_count++
}
END {
    if (oldf1 != 0)
    {
        summary = summary ";" oldf2 "=" f2_count
        printf("%s=%d%s\n", oldf1, f1_count, summary)
    }
}

将七行示例数据放入名为data的文件中,然后运行:

$ awk -f awk.script data
3277654321=4;33301=2;33310=1;33320=1
3291234567=3;33399=2;33301=1
$

让DBMS做更多工作

目前,数据类似于查询的输出,例如:

SELECT Field1, Field2
  FROM SomeTable
 ORDER BY Field1, Field2

通过让DBMS生成第一个字段,第二个字段的前5个字符以及条目数的计数,可以使报告的输出更好:

SELECT field1, SUBSTR(field2, 1, 5) AS field2, COUNT(*) AS number
  FROM SomeTable
 GROUP BY field1, field2
 ORDER BY field1, field2

然后通过网络传输的数据更少,如果数据库是远程的,这会有很大帮助。您还有一个更简单的报告。数据文件变为(data2):

3277654321 33301 2
3277654321 33310 1
3277654321 33320 1
3291234567 33399 2
3291234567 33301 1

awk脚本变为(awk.script2):

{   
    if (oldf1 != $1)
    {
        if (oldf1 != 0)
            printf("%s=%d%s\n", oldf1, f1_count, summary)
        oldf1 = $1
        f1_count = 0
        summary = ""
    }
    summary = summary ";" $2 "=" $3
    f1_count += $3
}
END {
    if (oldf1 != 0)
        printf("%s=%d%s\n", oldf1, f1_count, summary)
}

示例运行:

$ awk -f awk.script2 data2
3277654321=4;33301=2;33310=1;33320=1
3291234567=3;33399=2;33301=1
$

让DBMS做得更多

根据您的DBMS以及它是否支持子查询中的GROUP_CONCATORDER BY条款,您可以注意到rici suggested“这不是那么混乱,恕我直言”

  

以下似乎在SQLite3中运行良好;对于MySQL,您需要将逗号更改为GROUP_CONCAT中的单词SEPARATOR:

SELECT field1 || "=" || SUM(count2) || ";" ||
           group_concat(field2 || "=" || count2, ";") AS fields
  FROM (SELECT field1, SUBSTR(field2, 1, 5) AS field2, COUNT(*) AS count2
          FROM tmp
         GROUP BY field1, field2
         ORDER BY field1, field2
       )
 GROUP BY field1
 ORDER BY field1

请注意,据我所知,子标查询中的GROUP_CONCATORDER BY子句都不是由ISO标准SQL定义的,因此并非所有DBMS都支持这些功能。 (原因省略了ORDER BY功能,但推理不包括'正交性'的考虑因素。)

如果DBMS以您需要的格式生成数据,则不需要awk脚本对其进行后处理。什么是最好的将最终取决于你正在做什么。通常,使用DBMS在有意义的地方进行计算。 IMO,不要将DBMS用于所有格式化 - 我希望报告生成与分页等在DBMS之外完成 - 但是如果可以说服它生成所需的数据,那么无论如何都要让它完成工作。 / p>

答案 1 :(得分:0)

人们,我想分享一个“优雅”的解决方案。感谢飞往其他社区用户,他们推动我建议一些步骤。

awk     'NR>0   {C1[$1]++
                 C2[$1,substr($2,1,5)]++
                }
         END {for (c2 in C2) {split (c2, cx, SUBSEP); print cx[1] "=" C1[cx[1]] ";" cx[2] "=" C2[c2]}}
        ' SUBSEP=";" out.txt | sort | awk     '$1 != L        {printf "%s%s", LT, $1; L=$1; LT="\n"}
                        {printf ";%s", $2}
         END {printf "\n"}
        ' FS=";"

3277654321=4;33301=2;33310=1;33320=1
3291234567=3;33399=2;33301=1

而且rici,我不是要求有人为我编写代码。这只是一个大脚本的一小部分,所以我只是请求如何做一件小事的帮助。我对不同的方法感兴趣,这就是为什么我要求不提供任何代码示例。感谢所有参与此问题的SO用户,我仍然愿意尝试不同的方法。