Jak zrobić ciągłe wc -l z narzędziami tekstowymi GNU?

28

Wiem to oczywiście

cat logfile.txt | wc -l
120

powie mi liczbę wierszy w pliku.

Natomiast

tail -f logfile.txt

pokaże mi nowe wiersze, do których pisze inny program logfile.txt.

Czy można połączyć oba, aby uzyskać ciągłą aktualizację liczby wierszy pliku logfile.txt ze standardowymi narzędziami tekstowymi?

Wiem o tym

watch wc -l logfile.txt

ale nie chcę za każdym razem przeliczać całego pliku, co wydaje się marnotrawstwem. Potrzebny byłby tylko licznik dołączany co sekundę i prawdopodobnie \rzamiast \nna końcu linii.

towi
źródło
1
Czy Twój plik jest tak duży, że przeliczanie wszystkiego jest problemem? Jeśli chodzi o marnotrawstwo: catprodukcja rur do wcjest również dużym marnotrawstwem !!
Bernhard
Tak, potencjalnie jest bardzo duży.
towi

Odpowiedzi:

36

Może:

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

Uważaj, aby wyświetlał liczbę dla każdego wiersza danych wejściowych (chociaż przesłania poprzednią wartość, jeśli zostanie wysłana do terminala).

Lub możesz wdrożyć tail -fręcznie w powłoce:

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

(zwróć uwagę, że uruchamia się jedno wci jedno sleeppolecenie na sekundę, które nie zostały wbudowane we wszystkie powłoki. Gdy jest wbudowane ksh93while sleep, aby uzyskać wbudowane wc(przynajmniej w Debianie), musisz dodać /opt/ast/binz przodu $PATH(niezależnie od tego, czy ten katalog istnieje lub nie) lub użyj command /opt/ast/bin/wc(nie pytaj ...)).

Możesz użyć pv, jak w:

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

Ale uważaj, że dodaje k, M... sufiksy, gdy liczba przekracza 1000 (i wydaje się, że nie ma takiej możliwości ).

Stéphane Chazelas
źródło
Co do swojego tail | awkrozwiązania. Poznaj swoje opcje: -n +0nie przyszło mi do głowy w tej kombinacji.
towi
2
whoo! pv- kolejne przydatne nowe narzędzie. wielkie dzięki.
towi
Z grep możesz dodać filtr do swojego strumienia:tail -n +0 -f <my.log> | grep --line-buffered <mystring> | awk '{printf "\r%lu", NR}'
tombolinux
2
@tombolinux, awkjest nadzbiorem grep. tail -n +0 -f file | awk '/mystring/ {printf "\r%lu", ++n}'
Stéphane Chazelas
Fajne. Dodaję, END{print ""}aby awkwydrukować nowy wiersz na końcu.
pLumo,
6

Spróbuj policzyć to z czystym bashbez wc:

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

lub nawet w ten sposób, aby przepisać poprzednią wartość:

a=0 ; tail -f file | while read -r line ; do ((a++)) ; echo -ne "\r$a" ; done
pośpiech
źródło
1

Nie wierzę, że jest coś takiego. Ale powinno być łatwo wymieszać coś w stylu:

#!/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);
}

(Ogólny pomysł przeniesiony z perlfunc(1))

vonbrand
źródło
1
Liczba będzie rosła za każdym razem, gdy to zrobisz printf foo >> file. Będziesz musiał policzyć znaki nowego wiersza (jak wc -lma to miejsce w rozwiązaniu powłoki, które zasugerowałem), a nie rekordy zwrócone przez <$fh>. Nie sądzę, że musisz użyć telllub seekwcale.
Stéphane Chazelas,
<$fh>Odczytuje linię domyślnie nie zapisuje. Cytowana strona podręcznika Perla mówi, aby zrobić to w ten sposób ze względu na możliwie niechętne do współpracy środowisko (może to zależeć od systemu plików, sądzę, że NFS lub inne systemy plików podłączone do sieci mogą wymagać trochę prowokacji).
vonbrand
Wypróbuj sam, po osiągnięciu końca pliku, <$fh>zwróci rekord, nawet jeśli nie jest zakończony znakiem nowej linii. Więc jeśli perlsiedzi na końcu pliku, a ktoś później to zrobi printf foo >> file, to <$fh>wróci foo(nie wiersz, ponieważ nie jest zakończony znakiem nowego wiersza) i $countbędzie zwiększany, nawet jeśli do pliku nie dodano żadnej dodatkowej linii.
Stéphane Chazelas,
OP miał monitorować logi zapisywane po jednym wierszu?
vonbrand
Nie, dlatego Twoje rozwiązanie może nie działać. Na przykład, jeśli aplikacje zapisujące plik zbuforują jego dane wyjściowe, to w danym momencie ostatni wiersz prawdopodobnie nie zostanie zakończony, więc zostanie policzony dwukrotnie.
Stéphane Chazelas,
0

Kontynuacja rozwiązania opartego na awk: może nie być konieczne wyświetlanie licznika dla każdej linii w dzienniku; tak jest, możesz to zrobić w ten sposób (liczba zmienia się dla każdego 10 wierszy):

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