我有一个包含数千行的文件,每行包含一个数字,后跟一行文本。我想将文本相似的行的数字加起来。我也希望输出独特的线条。

例如:

25 cup of coffee
75 sign on the dotted
28 take a test
2 take a test
12 cup of coffee

输出将是:

37 cup of coffee
75 sign on the dotted
30 take a test

有关如何在unix shell中实现这一目标的任何建议吗?

我查看了Shell命令求和整数,每行一个?,但这是关于在文件的所有行中汇总一列数字,而不是仅仅在类似的文本行中。

分析解答

不需要多个进程和管道。仅awk就能够处理整个作业(并且在大文件上会快几个数量级)。使用awk,只需将每个字段2-NF附加为string,并将其用作索引,以对数组中字段1中的数字求和。然后在END部分中,简单地输出数组的内容,例如假设您的数据存储在file中,您可以这样做:

awk '{
    for (i=2; i<=NF; i++)
        str = str " " $i
    a[str] += $1
    str=""
}
END {
    for (i in a) print a[i], i
}' file

在上面,第一个for循环只是在str中附加​​2-NF的所有字段,a[str] += $1使用str作为索引将字段1中的值与a数组相加。这确保了相似线的值相加。在END部分中,您只需循环遍历输出元素值(总和)的数组的每个元素,然后循环索引(字段2-NF的原始str)。

例Use/Output

只需使用上面的内容,select,然后middle-mouse将其粘贴到file所在目录中的命令行中(将file的名称更改为您的数据文件名)

$ awk '{
>     for (i=2; i<=NF; i++)
>         str = str " " $i
>     a[str] += $1
>     str=""
> }
> END {
>     for (i in a) print a[i], i
> }' file
30  take a test
37  cup of coffee
75  sign on the dotted

如果您希望以不同的顺序排序行,只需在文件名后添加| sort [options]即可将输出通过管道输出到sort。例如,对于您显示的顺序输出,您将使用| sort -k 2,输出将是:

37  cup of coffee
75  sign on the dotted
30  take a test

保持字符串的原始顺序

根据您关于如何保留输入文件中看到的文本行的原始顺序的注释,您可以使用顺序索引保留第二个数组,其中字符串按照它们的顺序存储,以使它们保持有序。例如,下面使用o数组(顺序数组)来存储唯一的string(字段2-NF),并将变量n用作计数器。数组上的循环用于检查string是否已被包含,如果是,则使用next来避免存储string并跳转到下一个输入记录。在END中,循环然后使用for (i = 0; i < n; i++)形式以在原始文件中看到string的顺序从两个阵列输出信息,例如,

awk -v n=0 '{
    for (i=2; i<=NF; i++)
        str = str " " $i
    a[str] += $1
    for (i = 0; i < n; i++)
        if (o[i] == str) {
            str=""
            next;
        }
    o[n++] = str;
    str=""
}
END {
    for (i = 0; i < n; i++) print a[o[i]], o[i]
}' file

产量

37  cup of coffee
75  sign on the dotted
30  take a test