Como fazer um 'wc -l' contínuo com as ferramentas de texto gnu?

28

Eu sei é claro que

cat logfile.txt | wc -l
120

vai me dizer o número de linhas em um arquivo.

Enquanto que

tail -f logfile.txt

irá me mostrar as novas linhas que outro programa escreve logfile.txt.

É possível combinar os dois para que eu obtenha uma contagem de linhas de atualização contínua de logfile.txt com os utilitários de texto padrão?

Eu sei sobre

watch wc -l logfile.txt

mas não quero contar novamente o arquivo inteiro a cada vez, isso parece um desperdício. Seria necessário contar apenas uma contagem a cada segundo e, provavelmente, uma em \rvez de uma \nno final da linha.

towi
fonte
1
Seu arquivo é tão grande que contar tudo é um problema? Em termos de desperdício: a catprodução da tubulação wctambém é um grande desperdício !!
21283 Bernhard
Sim, potencialmente é muito grande.
towi

Respostas:

36

Talvez:

tail -n +1 -f file | awk '{printf "\r%lu", NR}'

Lembre-se de que ele produziria um número para cada linha de entrada (apesar de substituir o valor anterior se enviado para um terminal).

Ou você pode implementar o tail -fshell manualmente:

n=0
while :; do 
  n=$(($n + $(wc -l)))
  printf '\r%s' "$n"
  sleep 1
done < file

(observe que ele roda até um wce um sleepcomando por segundo, que nem todos os shells incorporaram. Com ksh93while sleep, o builtin é incorporado wc(pelo menos no Debian), você precisa adicionar /opt/ast/binna frente $PATH(independentemente de saber se esse diretório existe ou não) ou use command /opt/ast/bin/wc(não pergunte ...)).

Você pode usar pv, como em:

tail -n +1 -f file | pv -bl > /dev/null

Mas cuidado que ele acrescenta k, M... sufixos quando o número é mais de 1000 (e não parece ser uma maneira de contornar isso ).

Stéphane Chazelas
fonte
Whow à sua tail | awksolução. Conheça suas opções: -n +0não teria me ocorrido nessa combinação.
towi
2
whoo! pv- outra nova ferramenta útil. muito obrigado.
21313
Com o grep, você pode adicionar um filtro ao seu fluxo:tail -n +0 -f <my.log> | grep --line-buffered <mystring> | awk '{printf "\r%lu", NR}'
tombolinux
2
@tombolinux, awké um superconjunto de grep. tail -n +0 -f file | awk '/mystring/ {printf "\r%lu", ++n}'
Stéphane Chazelas
Legal. Eu adiciono END{print ""}para fazer awkuma nova linha de impressão no final.
PLumo # 16/18
6

Tente contar com puro bashsem wc:

a=0 ; tail -f file | while read -r line ; do ((a++)) ; echo $a ; done

ou mesmo assim para reescrever o valor anterior:

a=0 ; tail -f file | while read -r line ; do ((a++)) ; echo -ne "\r$a" ; done
pressa
fonte
1

Não acredito que exista algo assim. Mas deve ser fácil criar algo como:

#!/usr/bin/perl

$for_a_while = 1;

$oldcount = -1;
$count = 0;
open($fh, "<", $ARGV[0]);

for (;;) {
  for ($curpos = tell($fh); <$fh>; $curpos = tell($fh)) {
    $count++;
  }
  if($count != $oldcount) {
    print "$count\n";
    $oldcount = $count;
  }
  sleep($for_a_while);
  seek($fh, $curpos, 0);
}

(Idéia geral extraída de perlfunc(1))

vonbrand
fonte
1
O número aumentaria toda vez que você fizer a printf foo >> file. Você precisaria contar os caracteres de nova linha (como wc -lfaz na solução do shell que eu sugeri), não os registros retornados por <$fh>. Eu não acho que você precisa usar tellou seekem tudo.
Stéphane Chazelas
Ele <$fh>lê uma linha por padrão, não registros. A página de manual do Perl citada diz para fazê-lo dessa maneira, em prol de um ambiente possivelmente não cooperativo (pode depender do sistema de arquivos, acho que o NFS ou outros sistemas de arquivos montados em rede podem exigir um pouco de estímulo).
21133 vonbrand
Tente você mesmo, ao chegar ao final do arquivo, <$fh>retornará um registro mesmo que não tenha sido finalizado por um caractere de nova linha. Portanto, se perlestiver no final do arquivo, e alguém mais tarde fizer um printf foo >> file, <$fh>retornará foo(não uma linha, pois não foi finalizado por um caractere de nova linha) e $countserá incrementado mesmo que nenhuma linha extra tenha sido adicionada ao arquivo.
Stéphane Chazelas
OP deveria monitorar arquivos de log escritos uma linha de cada vez?
vonbrand
Não, é por isso que sua solução pode não funcionar. Por exemplo, se os aplicativos que gravam no arquivo armazenam em buffer sua saída, a qualquer momento, a última linha provavelmente não será encerrada e, portanto, será contada duas vezes.
Stéphane Chazelas
0

Continuando a solução baseada em awk: talvez você não precise ver o contador marcando cada linha em seu log; é esse o caso, você pode tê-lo assim (o número mudaria a cada 10 linhas):

tail -n +0 logfile.txt | \
    awk 'a+=1{}a%10==0{printf "\r%lu", a}END{printf "\r%lu", a}'
artyom
fonte