Policz liczbę pustych linii na końcu pliku

11

Mam plik z pustymi liniami na końcu pliku. Czy mogę użyć grepdo zliczenia liczby pustych linii na końcu pliku, przy czym nazwa pliku jest przekazywana jako zmienna w skrypcie?

Raghunath Choudhary
źródło
policzyć liczbę kolejnych pustych linii ?
RomanPerekhrest
2
@RomanPerekhrest Powiedziałbym tak, inaczej nie byliby „na końcu pliku”?
Sparhawk
„grep -cv -P” \ S „nazwa pliku” policzy całkowitą liczbę pustych linii w pliku. Tylko numer na końcu obciąża mój mózg!
MichaelJohn
OP poprosił o grepwygraną @MichaelJohn w mojej książce.
bu5hman
2
@ bu5hman Ale (jak przyznaje) nie odpowiada na pytanie. Tak naprawdę nie jest twoja.
Sparhawk

Odpowiedzi:

11

Jeśli puste linie są tylko na końcu

grep  -c '^$' myFile

lub:

grep -cx '' myFile
bu5hman
źródło
Pobity do edycji w sekundach, cholera
bu5hman
grep -cv . myFilejest innym sposobem na napisanie go (dla golfistów kodowych). Ale znalazłem rozwiązanie, grepjeśli gdziekolwiek w pliku są puste linie.
Philippos
2
@Filippos, grep -cv .zlicza również wiersze zawierające tylko bajty, które nie tworzą prawidłowych znaków.
Stéphane Chazelas
11

Dla zabawy, trochę upiorów sed:

#!/bin/sh
sed '/./!H;//h;$!d;//d;x;s/\n//' "$1" | wc -l

Wyjaśnienie:

  • /./adresuje linie dowolnym znakiem, więc /./!adresuje niepuste linie; w przypadku tych Hpoleceń polecenie dołącza je do miejsca wstrzymania. Zatem jeśli dla każdej pustej linii dodamy jedną linię do przestrzeni wstrzymania, zawsze jest o jedną linię więcej niż liczba pustych linii. Zajmiemy się tym później.
  • //hpusty wzorzec pasuje do ostatniego wyrażenia regularnego, którym był dowolny znak, więc każda niepusta linia jest adresowana i przenoszona do miejsca wstrzymania przez hpolecenie „zresetowania” zebranych linii do 1. Gdy zostanie dodany następny pusty wiersz, będą dwa, zgodnie z oczekiwaniami.
  • $!dzatrzymuje skrypt bez wyjścia dla każdego oprócz ostatniego wiersza, więc dalsze polecenia są wykonywane tylko po ostatnim wierszu. Więc wszystkie puste linie, które zebraliśmy w przestrzeni wstrzymania, znajdują się na końcu pliku. Dobrze.
  • //d: dPolecenie jest ponownie wykonywane tylko dla niepustych linii. Więc jeśli ostatni wiersz nie był pusty, sedwyjdzie bez żadnego wyjścia. Zero linii. Dobrze.
  • x Wymiany przechowują przestrzeń i przestrzeń wzoru, więc zebrane linie znajdują się teraz w przestrzeni wzoru, która ma zostać przetworzona.
  • Ale pamiętamy, że jest o jedną linię za dużo, więc zmniejszamy ją, usuwając jedną nową linię za pomocą s/\n//.
  • Voilà! Liczba linii odpowiada liczbie pustych linii na końcu (zwróć uwagę, że pierwsza linia nie będzie pusta, ale kogo to obchodzi), więc możemy ją policzyć wc -l.
Philippos
źródło
8

Więcej GNU tac/ tail -ropcji:

tac file | awk 'NF{exit};END{print NR?NR-1:0}'

Lub:

tac file | sed -n '/[^[:blank:]]/q;p' | wc -l

Zauważ, że na wyjściu:

printf 'x\n '

Oznacza to, że po ostatnim pełnym wierszu znajduje się dodatkowa spacja (którą niektórzy mogą uznać za dodatkową pustą linię, ale według definicji POSIX tekstu nie jest poprawnym tekstem), to dają 0.

POSIXly:

awk 'NF{n=NR};END{print NR-n}' < file

ale to oznacza odczytanie pliku w całości ( tail -r/ tacodczytałby plik do tyłu od końca na plikach, które można zobaczyć). To daje 1na wyjściu printf 'x\n '.

Stéphane Chazelas
źródło
6

Ponieważ faktycznie pytasz o greprozwiązanie , dodaję to, opierając się tylko na GNU grep(dobra, również przy użyciu składni powłoki i echo...):

#!/bin/sh
echo $(( $(grep -c "" "$1") - $(grep -B$(grep -cv . "$1") . "$1" |grep -c "") ))

Co ja tutaj robię? $(grep -c ".*" "$1")zlicza wszystkie linie w pliku, następnie odejmujemy plik bez końcowych pustych linii.

I jak je zdobyć? $(grep -B42 . "$1"grepuje wszystkie niepuste linie i 42 linie przed nimi, więc wypisze wszystko do ostatniej niepustej linii, o ile przed niepustą linią nie będzie więcej niż 42 kolejnych pustych linii. Aby uniknąć tego limitu, biorę $(grep -cv . "$1")jako parametr dla -Bopcji, która jest całkowitą liczbą pustych linii, więc zawsze wystarczająco dużą. W ten sposób usunąłem końcowe puste linie i mogę ich użyć |grep -c ".*"do zliczenia linii.

Genialne, prawda? (-;

Philippos
źródło
+1, ponieważ chociaż jest to okropny kod, technicznie odpowiada na zadane pytanie i nie mogę znieść oznaczenia cię ;-)
roaima
Grepmeister. Nie jesteśmy godni.
bu5hman
+1 za przewrotność. Inną (być może szybszą?) Opcją byłoby tac | greppierwsze niepuste z -m -A 42, a następnie minus jedna. Nie jestem pewien, która z nich jest bardziej wydajna, ale możesz też wc -l | cut -d' ' -f1zamiast wstawiać puste linie?
Sparhawk
Tak, oczywiście, można zrobić wiele rzeczy, z tac, wca cut, ale tutaj starałem się ograniczać do siebie grep. Możesz to nazwać przewrotnością, ja nazywam sportem. (-;
Philippos
5

Inne awkrozwiązanie Ta odmiana resetuje licznik za kkażdym razem, gdy pojawia się niepusta linia. Następnie każda linia zwiększa licznik. (Tak więc po pierwszej niepustej linii długości k==0.) Na końcu wyprowadzamy liczbę zliczonych linii.

Przygotuj plik danych

cat <<'X' >input.txt
aaa

bbb
ccc



X

Policz końcowe puste linie w próbce

awk 'NF {k=-1}; {k++}; END {print k+0}' input.txt
3

W tej definicji pusty wiersz może zawierać spacje lub inne puste znaki; wciąż jest pusty. Jeśli naprawdę chcesz liczyć puste linie zamiast pustych linii, zmień NFna $0 != "".

roaima
źródło
Dlaczego $0 > ""? To zastosowania, strcoll()które byłyby mniej wydajne niż te, $0 != ""które są używane memcmp()w wielu implementacjach (POSIX wymagał jednak, aby z niego korzystał strcoll()).
Stéphane Chazelas
@ StéphaneChazelas Nie uważałem, że $0 > ""może być inaczej $0 != "". I tak mam tendencję do traktowania awkjako „powolnego” operatora (na przykład, jeśli wiem, że mam duży zestaw danych jako danych wejściowych, a przetwarzanie ma krytyczne znaczenie dla czasu, zobaczę, co mogę zrobić, aby zmniejszyć ilość awkprzetwarzanych danych - ja używali grep | awkkonstruktów w takich sytuacjach). Jednakże, miał rzucić okiem na to, co zakładam jest definicja POSIX nie widzę żadnego odniesienia do jednej strcoll()lub memcmp(). czego mi brakuje?
roaima
strcoll()== ciągi należy porównać przy użyciu specyficznej dla danego regionu sekwencji zestawiania . Porównaj z poprzednią edycją . To ja to wychowywałem. Zobacz także austingroupbugs.net/view.php?id=963
Stéphane Chazelas
@ StéphaneChazelas implementacja, w której a <= b && a >= bniekoniecznie jest taka sama jak a == b. Auć!
roaima
To przypadek GNU awklub bash(za jego [[ a < b ]]operatorów) w en_US.UTF-8 lokalizacjach w systemach GNU na przykład za vs na przykład (na bashżaden <, >, =return true dla tych). Prawdopodobnie jest to błąd w definicji tych lokalizacji bardziej niż w bash / awk
Stéphane Chazelas
2

policzyć liczbę kolejnych pustych linii na końcu pliku

Solid awk+ tacrozwiązanie:

Próbka input.txt:

$ cat input.txt
aaa

bbb
ccc



$  # command line 

Akcja:

awk '!NF{ if (NR==++c) { cnt++ } else exit }END{ print int(cnt) }' <(tac input.txt)
  • !NF- zapewnia, że ​​bieżąca linia jest pusta (nie ma pól)
  • NR==++c- zapewnienie kolejności pustych wierszy. ( NR- numer rekordu, ++c- równomiernie zwiększany licznik pomocniczy)
  • cnt++- licznik pustych linii

Wyjście:

3
Roman Perekhrest
źródło
1

IIUC, następujący skrypt o nazwie wykonałby count-blank-at-the-end.shzadanie:

#!/usr/bin/env sh

count=$(tail -n +"$(grep . "$1" -n | tail -n 1 | cut -d: -f1)" "$1" | wc -l)
num_of_blank_lines=$((count - 1))

printf "%s\n" "$num_of_blank_lines"

Przykładowe użycie:

$ ./count-blank-at-the-end.sh FILE
4

Testowałem go GNU bash, Android mkshaw ksh.

Arkadiusz Drabczyk
źródło