Zählen Sie die Anzahl der Leerzeilen am Ende der Datei


11

Ich habe eine Datei mit Leerzeilen am Ende der Datei. Kann ich grepdie Anzahl der Leerzeilen am Ende der Datei zählen, wobei der Dateiname im Skript als Variable übergeben wird?


um die Anzahl der aufeinanderfolgenden Leerzeilen zu zählen ?
RomanPerekhrest

2
@RomanPerekhrest Ich würde es sagen, sonst wären sie nicht "am Ende der Datei"?
Sparhawk

'grep -cv -P' \ S 'Dateiname' zählt die Gesamtzahl der Leerzeilen in der Datei. Die Zahl am Ende belastet nur mein Gehirn!
Michael John

OP fragte nach grep@MichaelJohn gewinnt für Reinheit in meinem Buch.
Bu5hman

2
@ bu5hman Aber (wie er zugibt) beantwortet die Frage nicht. Deiner auch nicht wirklich.
Sparhawk

Antworten:


11

Wenn die Leerzeilen nur am Ende stehen

grep  -c '^$' myFile

oder:

grep -cx '' myFile

Von Sekunden auf die Bearbeitung geschlagen, verdammt
bu5hman

grep -cv . myFileist eine andere Art, es zu schreiben (für Code-Golfer). Aber ich habe eine Lösung gefunden, grepwenn es irgendwo in der Datei leere Zeilen gibt.
Philippos

2
@Philippos grep -cv .würde auch die Zeilen zählen, die nur Bytes enthalten, die keine gültigen Zeichen bilden.
Stéphane Chazelas

11

Nur zum Spaß, einige gruselig sed:

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

Erläuterung:

  • /./adressiert Zeilen mit einem beliebigen Zeichen, /./!adressiert also nicht leere Zeilen; Für diese Hhängt der Befehl sie an den Haltebereich an. Wenn wir also für jede leere Zeile eine Zeile zum Haltebereich hinzugefügt haben, gibt es immer eine Zeile mehr als die Anzahl der leeren Zeilen. Wir werden uns später darum kümmern.
  • //hDas leere Muster entspricht dem letzten regulären Ausdruck, bei dem es sich um ein beliebiges Zeichen handelt. Daher wird jede nicht leere Zeile adressiert und durch den Befehl in den Haltebereich verschoben , um hdie gesammelten Zeilen auf 1 zurückzusetzen. Wenn die nächste leere Zeile angehängt wird, Wie erwartet wird es wieder zwei geben.
  • $!dStoppt das Skript ohne Ausgabe für jede bis auf die letzte Zeile, sodass weitere Befehle erst nach der letzten Zeile ausgeführt werden. Alle leeren Zeilen, die wir im Haltebereich gesammelt haben, befinden sich am Ende der Datei. Gut.
  • //d: Der dBefehl wird erneut nur für nicht leere Zeilen ausgeführt. Wenn also die letzte Zeile nicht leer war, sedwird sie ohne Ausgabe beendet. Nulllinien. Gut.
  • x Der Austausch enthält Speicherplatz und Musterraum, sodass sich die gesammelten Zeilen im Musterraum befinden, der jetzt verarbeitet werden soll.
  • Wir erinnern uns jedoch daran, dass eine Zeile zu viel ist, und reduzieren sie, indem wir eine neue Zeile mit entfernen s/\n//.
  • Voilà! Die Anzahl der Zeilen entspricht der Anzahl der leeren Zeilen am Ende (beachten Sie, dass die erste Zeile nicht leer ist, aber wen interessiert das?), Damit wir sie zählen können wc -l.

8

Einige weitere GNU tac/ tail -rOptionen:

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

Oder:

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

Beachten Sie, dass auf der Ausgabe von:

printf 'x\n '

Das heißt, wenn nach der letzten vollständigen Zeile ein zusätzliches Leerzeichen steht (was einige als zusätzliche Leerzeile betrachten könnten, aber nach der POSIX-Definition von Text kein gültiger Text ist), würden diese 0 ergeben.

POSIXly:

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

Dies bedeutet jedoch, dass die Datei vollständig gelesen wird ( tail -r/ tacwürde die Datei bei durchsuchbaren Dateien vom Ende rückwärts lesen). Das gibt 1auf die Ausgabe von printf 'x\n '.


6

Da Sie tatsächlich nach einer grepLösung fragen, füge ich diese hinzu, die nur auf GNU grepbasiert (okay, auch mit Shell-Syntax und echo...):

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

Was mache ich hier? $(grep -c ".*" "$1")zählt alle Zeilen in der Datei, dann subtrahieren wir die Datei ohne die nachfolgenden leeren Zeilen.

Und wie bekommt man die? $(grep -B42 . "$1"würde alle nicht leeren Zeilen und 42 Zeilen vor ihnen erfassen, so dass alles bis zur letzten nicht leeren Zeile gedruckt wird, solange nicht mehr als 42 aufeinanderfolgende leere Zeilen vor einer nicht leeren Zeile stehen. Um diese Grenze zu vermeiden, nehme ich $(grep -cv . "$1")als Parameter für die -BOption die Gesamtzahl der leeren Zeilen, also immer groß genug. Auf diese Weise habe ich die nachgestellten leeren Zeilen entfernt und kann sie |grep -c ".*"zum Zählen der Zeilen verwenden.

Genial, nicht wahr? (-;


+1, denn obwohl das ein schrecklicher Code ist, beantwortet er die gestellte Frage technisch und ich kann es nicht ertragen, dich zu markieren
;-)

Grepmeister. Wir sind es nicht wert.
Bu5hman

+1 für die Perversität. Eine andere (möglicherweise schnellere?) Option wäre, tac | grepzum ersten nicht leeren mit -m -A 42, dann minus eins. Ich bin mir nicht sicher, was effizienter ist, aber Sie könnten auch, wc -l | cut -d' ' -f1anstatt die leeren Zeilen zu greifen?
Sparhawk

Ja, sicher, Sie können viele Dinge damit machen tac, wcund cut, aber hier habe ich versucht, mich darauf zu beschränken grep. Man kann es Perversität nennen, ich nenne es Sport. (-;
Philippos

5

Eine andere awkLösung. Diese Variante setzt den Zähler zurückk jedes Mal zurück, wenn eine nicht leere Zeile vorhanden ist. Dann erhöht jede Zeile den Zähler. (Also nach der ersten nicht leeren Zeile k==0.) Am Ende geben wir die Anzahl der Zeilen aus, die wir gezählt haben.

Bereiten Sie die Datendatei vor

cat <<'X' >input.txt
aaa

bbb
ccc



X

Zählen Sie die nachfolgenden Leerzeilen in der Probe

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

In dieser Definition kann eine Leerzeile Leerzeichen oder andere Leerzeichen enthalten. es ist immer noch leer. Wenn Sie wirklich leere Zeilen anstatt leere Zeilen zählen möchten, ändern Sie NFfür $0 != "".


Warum $0 > ""? Diese Verwendungen, strcoll()die weniger effizient wären als $0 != ""die Verwendungen memcmp()in vielen Implementierungen (POSIX erforderte jedoch die Verwendung strcoll()).
Stéphane Chazelas

@ StéphaneChazelas Ich habe nicht gedacht, dass $0 > ""das anders sein könnte als $0 != "". Ich neige awksowieso dazu, als "langsamer" Operator zu behandeln (wenn ich weiß, dass ich einen großen Datensatz als Eingabe habe und die Verarbeitung zeitkritisch ist, werde ich sehen, was ich tun kann, um die awkzu verarbeitende Menge zu reduzieren - ich grep | awkin solchen Situationen Konstrukte verwendet haben ). Um jedoch einen kurzen Blick auf das, was gehabt zu haben ich nehme an, das ist POSIX Definition ich keinen Hinweis auf entweder sehen strcoll()oder memcmp(). Was vermisse ich?
Roaima

strcoll()== Die Zeichenfolgen werden anhand der länderspezifischen Sortierfolge verglichen . Vergleiche mit der vorherigen Ausgabe . Ich war derjenige, der es ansprach. Siehe auch austingroupbugs.net/view.php?id=963
Stéphane Chazelas

@ StéphaneChazelas eine Implementierung, bei der a <= b && a >= bnicht unbedingt die gleiche ist wie a == b. Autsch!
Roaima

Dies ist der Fall bei GNU awkoder bash(für seine [[ a < b ]]Operatoren) in en_US.UTF-8-Gebietsschemas auf GNU-Systemen, zum Beispiel für vs (für bash, keines von <, >gibt =für diese true zurück). Wahrscheinlich ist es ein Fehler in der Definition dieser Gebietsschemas mehr als in bash / awk
Stéphane Chazelas

2

um die Anzahl aufeinanderfolgender Leerzeilen am Ende der Datei zu zählen

Fest awk+ tacLösung:

Beispiel input.txt:

$ cat input.txt
aaa

bbb
ccc



$  # command line 

Die Aktion:

awk '!NF{ if (NR==++c) { cnt++ } else exit }END{ print int(cnt) }' <(tac input.txt)
  • !NF- stellt sicher, dass die aktuelle Zeile leer ist (keine Felder hat)
  • NR==++c- Sicherstellen der fortlaufenden Reihenfolge der Leerzeilen. ( NR- Datensatznummer,++c - gleichmäßig inkrementierter Hilfszähler)
  • cnt++- Zähler von Leerzeilen

Die Ausgabe:

3

1

IIUC, das folgende Skript count-blank-at-the-end.shwürde den Job erledigen:

#!/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"

Anwendungsbeispiel:

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

Getestet habe ich es in GNU bash, Android mkshund in ksh.


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.