我想使用bash只添加第一列的计数,而不执行uniq,如下所示:
输入:
58311s2727 NC_000082.6 100.00 50
58311s2727 NC_000083.6 100.00 60
58311s2727 NC_000084.6 100.00 70
58310s2691 NC_000080.6 100.00 30
58310s2691 NC_000081.6 100.00 20
58308s2441 NC_000074.6 100.00 50
输出:
3 58311s2727 NC_000082.6 100.00 50
3 58311s2727 NC_000083.6 100.00 60
3 58311s2727 NC_000084.6 100.00 70
2 58310s2691 NC_000080.6 100.00 30
2 58310s2691 NC_000081.6 100.00 20
1 58308s2441 NC_000074.6 100.00 50
我试过了:
sort input.txt | cut -f1 | uniq -c
但输出不是我想要的。我想知道是否有简单的方法来解决这个问题。
答案 0 :(得分:1)
使用排序输入,您只需使用awk
,捕获具有相同键的行集,并在键更改时打印上一个输出。处理EOF有点混乱;你必须重复打印。你可以编写一个awk
函数来进行打印,但这对于这么简单的事情来说几乎是过度的。
script.awk
$1 != old_key { if (n_keys > 0) for (i = 0; i < n_keys; i++) print n_keys, saved[i]; n_keys = 0 }
{ saved[n_keys++] = $0; old_key = $1 }
END { if (n_keys > 0) for (i = 0; i < n_keys; i++) print n_keys, saved[i] }
对于示例输入input.txt
(已经分组),输出为:
$ awk -f script.awk input.txt
3 58311s2727 NC_000082.6 100.00 50
3 58311s2727 NC_000083.6 100.00 60
3 58311s2727 NC_000084.6 100.00 70
2 58310s2691 NC_000080.6 100.00 30
2 58310s2691 NC_000081.6 100.00 20
1 58308s2441 NC_000074.6 100.00 50
$
如果您希望对其进行排序,请先对其进行排序:
$ sort input.txt | awk -f script.awk
1 58308s2441 NC_000074.6 100.00 50
2 58310s2691 NC_000080.6 100.00 30
2 58310s2691 NC_000081.6 100.00 20
3 58311s2727 NC_000082.6 100.00 50
3 58311s2727 NC_000083.6 100.00 60
3 58311s2727 NC_000084.6 100.00 70
$
请注意,除了其他优点之外,这可以处理来自管道的数据,因为它不需要处理文件两次,这与当前接受的至少一个其他解决方案不同。它只在内存中保留尽可能多的行,因为在公共密钥的最大组中存在行,因此即使是相当大的文件也不太可能对系统上的内存造成压力。 (sort
可能会比awk
承担更多的内存负载。)
script2.awk
使用函数和一些空格,代码变为:
function dump_keys( i) {
if (n_keys > 0)
{
for (i = 0; i < n_keys; i++)
print n_keys, saved[i]
}
n_keys = 0
}
$1 != old_key { dump_keys() }
{ saved[n_keys++] = $0; old_key = $1 }
END { dump_keys() }
变量i
是函数的本地变量(awk
的怪癖)。我可以简单地从参数列表中省略它,因为脚本中的其他地方没有使用i
。
这产生与script.awk
相同的输出。
答案 1 :(得分:0)
如果没有uniq
,您必须阅读输入两次。在纯BASH中有一些方法可以做到这一点,但是当我切换到像Python 2这样的正确脚本语言时:
import codecs
from collections import Counter
filename='...'
encoding='...' # file encoding
counter = Counter()
with codecs.open(filename, 'r', encoding) as fh:
for line in fh:
parts = line.split(' ')
counter[parts[0]] += 1
with codecs.open(filename, 'r', encoding) as fh:
for line in fh:
parts = line.split(' ')
count = counter[parts[0]]
print '%d%s' % (count, line),
答案 2 :(得分:0)
我会在awk中这样做。但正如Aaron所说,它需要两次读取输入,因为你第一次击中特定的一行时,你不知道有多少次它会击中它。
printf
第一次通过文件,用第一个字段的计数器填充数组。然后它再次进行,打印计数和每一行。
您可以调整$ declare -A a
$ while read word therest; do ((a[$word]++)); done < inputfile
$ while read word therest; do printf "%5d\t%s\t%s\n" "${a[$word]}" "$word" "$therest"; done < inputfile
语句以满足您的格式要求(可能将其替换为declare -A
)。
如果你不想使用awk,并且真的希望它在bash中本地工作,你可以使用几个带有for循环的单行来实现几乎相同的结果:
$a
awk
是必需的,因为c.on('data', function(data) {
if (data.toString().charCodeAt(0) === 3) {
c.destroy();
}
});
需要是一个关联数组,每行的第一个单词作为键。另一方面,SaveToFile
将每个数组视为关联数组。请注意,此解决方案不会保留您的空白。