Linux 在bash中对不带uniq的列进行排序和计数

Linux 在bash中对不带uniq的列进行排序和计数,linux,bash,sorting,count,sh,Linux,Bash,Sorting,Count,Sh,我只想使用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

我只想使用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

但是输出不是我想要的。我想知道是否有简单的方法来解决这个问题。

如果没有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),

我会在awk里做这件事。但是正如Aaron所说,它需要读取输入两次,因为当你第一次点击某一行时,你不知道还会点击多少次

$ awk 'NR==FNR{a[$1]++;next} {print a[$1],$0}' inputfile inputfile
这是第一次遍历文件,用第一个字段的计数器填充数组。然后再进行第二次,打印每行的计数

您可以调整
print
语句以满足您的格式要求(可能将其替换为
printf


如果您不想使用awk,并且确实希望它在bash中本机工作,那么可以使用两个带有for循环的单行程序来实现几乎相同的结果:

$ 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
$whilereadwordtherest;do((一个[$word]++);完成<输入文件
$whilereadwordtherest;是否打印“%5d\t%s\t%s\n”“${a[$word]}”“$word”“$therest”;完成<输入文件

declare-A
是必需的,因为
$A
需要是一个关联数组,每行的第一个字作为键<另一方面,code>awk将每个数组视为关联数组。请注意,此解决方案不会保留您的空白。

对于排序输入,您只需使用
awk
,捕获具有相同键的行集,并在键更改时打印先前设置的行集。处理EOF有点混乱;你必须重复打印。您可以编写一个
awk
函数来进行打印,但对于这样简单的事情来说,这几乎是多余的

script.awk
如果要对其进行排序,请先对其进行排序:

$ 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
$
请注意,除了其他优点外,它还可以处理来自管道的数据,因为它不需要处理文件两次,这与目前接受的至少一种其他解决方案不同。它在内存中保留的行数也仅与公共密钥最大组中的行数相同,因此即使是相当大的文件也不太可能在系统上增加内存。(排序可能比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){
如果(n_键>0)
{
对于(i=0;i
变量
i
是函数的局部变量(一个
awk
)。我可以简单地从参数列表中省略它,因为脚本中的其他地方没有使用
I


这会产生与
script.awk

相同的输出,您可以发布任何您尝试过的解决方案吗?我尝试过:sort input.txt | cut-f1 | uniq-c,但输出不是我想要的,想知道是否有简单的方法来解决这个问题如果您想使用uniq,您可以这样做:
awk'{print$1}|uniq-c
如果你真的不想使用uniq,你可以这样做:
awk'{count[$1]+}END{for(j in count)print j,count[j]}
awk'{count[$1]+}END{for(j in count)print j,count[j]}将执行类似于| uniq-c的操作,它将删除所有其他列,并删除所有重复,这不是我想要的。这个问题看起来不错。我也使用python,但我的输入量很大,想知道是否有更简单的方法使用pythonbash@once:定义大的。就像兆字节、千兆字节、兆字节或更大的字节一样?@JonathanLeffler以兆字节为单位,但我有一些是正常的;而“少数”是数十、数百、数千、数百万个这样的文件?现在兆字节的数据不是很大。是的。。我的数据达到了千兆,但我没有一台好的计算机,所以必须分成更小的文件,而且bash在我的情况下运行得最快谢谢!!这正是我想要的!awk'NR==FNR{a[$1]++;next}{printf(“%5s\t%s\n”,a[$1],$0)}'input.txt input.txt>output。txt@once:请注意,这不能用于不在文件中的数据(例如,在管道中),因为它必须能够读取相同的数据两次。它还将所有密钥存储在内存中,这可能不是什么大问题,但值得注意。当然,读取一个文件两次要比读取一次昂贵,而真正需要的就是读取一次。对其有利的是,即使数据未按输入排序,该技术也能正常工作。当然,在这种情况下,输出也不会排序。
$ 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
$
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() }