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 命令对整数求和,每行一个?但这是对文件中所有行的一列数字进行求和,而不仅仅是对相似的文本行进行求和。
不需要多个进程和管道。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 中的所有字段str,a[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)