Ich habe eine Datei mit Leerzeilen am Ende der Datei. Kann ich grep
die Anzahl der Leerzeilen am Ende der Datei zählen, wobei der Dateiname im Skript als Variable übergeben wird?
grep
@MichaelJohn gewinnt für Reinheit in meinem Buch.
Ich habe eine Datei mit Leerzeilen am Ende der Datei. Kann ich grep
die Anzahl der Leerzeilen am Ende der Datei zählen, wobei der Dateiname im Skript als Variable übergeben wird?
grep
@MichaelJohn gewinnt für Reinheit in meinem Buch.
Antworten:
Wenn die Leerzeilen nur am Ende stehen
grep -c '^$' myFile
oder:
grep -cx '' myFile
grep -cv . myFile
ist eine andere Art, es zu schreiben (für Code-Golfer). Aber ich habe eine Lösung gefunden, grep
wenn es irgendwo in der Datei leere Zeilen gibt.
grep -cv .
würde auch die Zeilen zählen, die nur Bytes enthalten, die keine gültigen Zeichen bilden.
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 H
hä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.//h
Das 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 h
die gesammelten Zeilen auf 1 zurückzusetzen. Wenn die nächste leere Zeile angehängt wird, Wie erwartet wird es wieder zwei geben.$!d
Stoppt 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 d
Befehl wird erneut nur für nicht leere Zeilen ausgeführt. Wenn also die letzte Zeile nicht leer war, sed
wird 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.s/\n//
.wc -l
.Einige weitere GNU tac
/ tail -r
Optionen:
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
/ tac
würde die Datei bei durchsuchbaren Dateien vom Ende rückwärts lesen). Das gibt 1
auf die Ausgabe von printf 'x\n '
.
Da Sie tatsächlich nach einer grep
Lösung fragen, füge ich diese hinzu, die nur auf GNU grep
basiert (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 -B
Option 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? (-;
tac | grep
zum 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' ' -f1
anstatt die leeren Zeilen zu greifen?
tac
, wc
und cut
, aber hier habe ich versucht, mich darauf zu beschränken grep
. Man kann es Perversität nennen, ich nenne es Sport. (-;
Eine andere awk
Lö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 NF
für $0 != ""
.
$0 > ""
? Diese Verwendungen, strcoll()
die weniger effizient wären als $0 != ""
die Verwendungen memcmp()
in vielen Implementierungen (POSIX erforderte jedoch die Verwendung strcoll()
).
$0 > ""
das anders sein könnte als $0 != ""
. Ich neige awk
sowieso 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 awk
zu verarbeitende Menge zu reduzieren - ich grep | awk
in 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?
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
a <= b && a >= b
nicht unbedingt die gleiche ist wie a == b
. Autsch!
awk
oder 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
um die Anzahl aufeinanderfolgender Leerzeilen am Ende der Datei zu zählen
Fest awk
+ tac
Lö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 LeerzeilenDie Ausgabe:
3
IIUC, das folgende Skript count-blank-at-the-end.sh
wü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 mksh
und in ksh
.
Alternative Python
Lösung:
Beispiel input.txt:
$ cat input.txt
aaa
bbb
ccc
$ # command line
Die Aktion:
python -c 'import sys, itertools; f=open(sys.argv[1]);
lines=list(itertools.takewhile(str.isspace, f.readlines()[::-1]));
print(len(lines)); f.close()' input.txt
Die Ausgabe:
3
https://docs.python.org/3/library/itertools.html?highlight=itertools#itertools.takewhile