Shell 命令对文件中相似文本行的数字进行求和

Ton*_*ony 3 bash shell scripting

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

例如:

25 cup of coffee
75 sign on the dotted
28 take a test
2 take a test
12 cup of coffee
Run Code Online (Sandbox Code Playgroud)

输出将是:

37 cup of coffee
75 sign on the dotted
30 take a test
Run Code Online (Sandbox Code Playgroud)

有什么建议如何在 unix shell 中实现这一点吗?

我查看了Shell 命令对整数求和,每行一个?但这是对文件中所有行的一列数字进行求和,而不仅仅是对相似的文本行进行求和。

Dav*_*ica 5

不需要多个进程和管道。awk单独一个就足以处理整个工作(并且在处理大文件时速度会快几个数量级)。只需awk将每个字段附加2-NF为字符串,并使用它作为索引来对数组中字段 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
Run Code Online (Sandbox Code Playgroud)

上面,第一个for循环只是附加了2-NFin 中的所有字段stra[str] += $1将字段 1 中的值求和到数组中a并用作str索引。这确保了相似行的值被求和。在本END节中,您只需循环遍历数组的每个元素,输出元素值(总和),然后输出索引(strfields 的原始值2-NF)。

使用/输出示例

只需采取上面的内容,选择它,然后用鼠标中键将其粘贴到您所在目录的命令行中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
Run Code Online (Sandbox Code Playgroud)

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

37  cup of coffee
75  sign on the dotted
30  take a test
Run Code Online (Sandbox Code Playgroud)

保留字符串的原始顺序

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

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
Run Code Online (Sandbox Code Playgroud)

输出

37  cup of coffee
75  sign on the dotted
30  take a test
Run Code Online (Sandbox Code Playgroud)