Exibe valores distintos de uma lista e número de ocorrências

1

Eu tenho um programa que escreve na saída padrão uma lista de valores de string, um por linha, e eu gostaria de exibir em tempo real a lista de valores distintos junto com o número de ocorrências para cada um.

Por exemplo, desta saída:

apple
carrot
pear
carrot
apple

Eu preciso de um comando que gere essa saída (idealmente atualizada em tempo real):

apple: 2
carrot: 2
pear: 1

Alguma ideia?

    
por fiddler 29.01.2015 / 03:57

3 respostas

3

Para expandir o que o @Bratchley disse nos comentários, se você tiver a impressão de saída do seu programa em um arquivo, poderá executar o comando watch no terminal para obter uma exibição quase em tempo real da saída incluindo a bandeira -n da seguinte forma:

watch -n 0.1 "cat yourprograms.log | sort | uniq -c | sort -rn"

Note: The ' -n ' flag sets the refresh interval. The minimum time ' watch ' can take is 0.1 second (or 1/10 a second) and nothing smaller.

Exemplo de saída:

Every 0.1s: cat yourprograms.log | sort | uniq -c | sort -rn

      6 things
      4 mine
      3 dot
      1 below

A inclusão de | sort -rn permite uma visualização de classificação melhor. sort -rn classifica a saída de uniq -c na ordem numérica reversa.

Se você só quer saber o top 10, pode incluir o comando head da seguinte forma:

watch -n 0.1 "cat yourprograms.log | sort | uniq -c | sort -rn | head"
    
por 29.01.2015 / 06:39
1

Aqui está um pequeno programa do Python 2 que faz o que você quer. As palavras são listadas em ordem cronológica de primeira aparição, ou seja, cada nova palavra é adicionada à parte inferior da lista, mas seria fácil classificar as palavras em ordem alfabética ou por ordem de número de ocorrências.

A saída pode ser um pouco mais simples, por exemplo, se você souber o tamanho máximo da string, os números de ocorrências poderão ser alinhados em uma coluna.

wordcount.py

#! /usr/bin/env python

''' Real-time word counting

    Written by PM 2Ring 2015.01.29
    From http://unix.stackexchange.com/q/181722/88378
'''

CSI = '\x1b['
clear = CSI + '2J' + CSI + 'H'

def main():
    words = []
    wordcount = {}
    while True:
        try:
            word = raw_input()
        except (KeyboardInterrupt, EOFError):
            print 
            break

        if word not in wordcount:
            words.append(word)
            wordcount[word] = 1
        else:
            wordcount[word] += 1

        print clear
        for word in words:
            print '%s: %d' % (word, wordcount[word])

if __name__ == '__main__':
    main()

Para enviar a saída de yourprog para este programa, você pode fazer isso:

yourprog | python wordcount.py

assumindo que wordcount.py esteja no diretório atual.

Como alternativa, forneça wordcount.py de permissão de execução (por exemplo, chmod a+x wordcount.py ) e coloque-a em um diretório em sua lista PATH de comando (ou seja, um diretório listado quando você faz echo "$PATH" ). Então você pode fazer

yourprog | wordcount.py

e você pode executá-lo em qualquer lugar.

Se você tiver apenas o Python 3 e não o Python 2, o programa exigirá algumas pequenas alterações para executá-lo.

FWIW, aqui está outro script Python que escrevi para testar o código acima. Imprime uma palavra aleatória, uma palavra por linha, a cada 0,5 segundos.

randwords.py

#! /usr/bin/env python

import random, time

wordlist = [
    'apple',
    'carrot',
    'pear',
    'orange',
    'banana',
    'cabbage',
    'potato'
]

def main():
    #Time delay between words, in seconds
    delay = 0.5
    while True:
        try:
            print random.choice(wordlist)
            time.sleep(delay)
        except KeyboardInterrupt:
            print 
            break

if __name__ == '__main__':
    main()

Você pode executá-lo, colocando sua saída em wordcount.py da seguinte forma:

python -u randwords.py | python wordcount.py

O sinal -u diz ao interpretador Python para usar E / S sem buffer.

    
por 29.01.2015 / 07:04
1

Coisa simples de awk. Usando algumas sequências ANSI para movimento e limpeza ...

#!/usr/bin/awk -f

BEGIN {
    print "Stats:\n---------------------------------"
}
function clear() {
    for (k in ar)
        printf "\r3[K3[1A"
}
function stats() {
    for (k in ar)
        printf "%-10s: %d\n", k, ar[k]
}
/./{
    clear()
    if (!ar[$0])
        ar[$0]=1
    else
        ++ar[$0]
    stats()
}

Gerador de saída de amostra:

#!/bin/bash

declare -a fr=(
    apple
    carrot
    pear
)

range=${#fr[@]}

while ((1)); do
    ((x = RANDOM % range))
    printf "%s\n" "${fr[$x]}"
    sleep .5
done

Executar como:

$ ./fruit_script | ./awk_script

Estendido com cores, cursor oculto, etc.:

#!/bin/bash

quit()
{
    printf "\r3[K3[?25h"
}

trap quit SIGINT

awk '
    BEGIN {
        width=3
        printf "3[?25l"
        print "Stats: (Ctrl-c to quit)\n---------------------------------"
    }
    function clear() {
        for (k in ar)
            printf "\r3[K3[1A"
    }
    function stats() {
        for (k in ar)
            printf "3[0;34m%-*s3[0m: 3[1;31m%d3[0m\n", width, k, ar[k]
    }
    /./{
        if (length($0) + 1 > width)
            width=length($0) + 1
        clear()
        if (!ar[$0])
            ar[$0]=1
        else
            ++ar[$0]

        stats()
    }
'
    
por 29.01.2015 / 08:01