2016-10-12 5 views
1

Ich habe eine Datei, die Daten wie folgt enthält:Wie bestimmt man die Anzahl der Zeilen zwischen zwei Strings mit Bash und Standard-Utilities?

abc 
abc, Iteration 1 
abc 
abc, Iteration 2 
... 
abc 
abc, Iteration 19 
abc 
abc, Iteration 20 

Ich mag die Anzahl von Zeilen zwischen den Zeilen bestimmen, das genau in den Saiten Ende „Iteration 1“ und „Iteration 2“ und speichere die Anzahl von Linien auf die Variable numlines. In dem obigen Beispiel sollte numlines den Wert enthalten 1.

Ich möchte wc -l, verwenden sed oder awk.

+2

Check-out http://stackoverflow.com/questions/38972736/how-to-select-lines-between-two- Muster zum Extrahieren der Linien zwischen den beiden Mustern (auch ohne die Muster) und weiterleiten an "wc" oder wahrscheinlich einen Zähler in "awk" Lösung selbst verwenden – Sundeep

Antwort

4

Vijay's helpful sed answer ist kurz, aber immer verarbeitet die gesamten Eingabedatei (und schafft auch zusätzliche Kindprozesse, weil wc -l muss auch geltend gemacht werden - obwohl das wird kaum Materie insgesamt).

Versuchen Sie, die folgenden awk Lösung, die verlässt, sobald das Ende des Bereichs gefunden wird (es schafft auch nur ein einziges Kind-Prozess - die Subshell weg zugunsten des einfachen awk Befehl optimiert ist); mit großen Eingabedateien kann, diese Rolle, je nachdem, wo in der Datei der Bereich positioniert ist:

numlines=$(awk '/Iteration 1$/ {b=NR; next} /Iteration 2$/ {print NR-b-1; exit}' file) 

Spitze des Hutes zu karakfa zu helfen, um den Befehl zu optimieren.

Hinweis: /Iteration 1$/ und /Iteration 2$/ sind reguläre Ausdrücke dass Matchstrings Iteration 1 und Iteration 2 am Ende einer Zeile ($).
Die vorhandenen Zeichenfolgen enthalten keine Metazeichen für reguläre Ausdrücke, die zu escaping (mit \) müssen, aber Sie müssen das in anderen Fällen möglicherweise tun.
Wenn die übereinstimmenden Strings nicht im Voraus Literale bekannt sind, wäre generisches Escaping schwierig; in diesem Fall, betrachten Ed Morton's solution, die auf Strings, nicht regulären Ausdrücken basiert.

+2

Warum nicht?/Iteration 1 $/{b = NR}/Iteration 2 $/{print NR-b-1; exit} ' – karakfa

+0

@karakfa: Ausgezeichneter Punkt, danke - Antwort aktualisiert. – mklement0

+0

@EdMorton: Ja, die im Befehl verwendeten regulären Ausdrücke stimmen mit Zeilen überein, die genau in den Strings "Iteration 1" und "Iteration 2" enden, wie gewünscht. – mklement0

3
sed '/Iteration\ 1/,/Iteration\ 2/!d;//d' filename | wc -l 
+0

Viel prägnanter als meins :) – tink

+1

Dies scheint zu brechen, wenn die Datei enthält "Iteration 10" und "Iteration 20" usw. – IslandPatrol

+0

@ mklement0 Ich habe die Beispieldatei in meiner Frage aktualisiert, um die Art der Daten wiederzugeben, die die Lösung behandeln sollte. – IslandPatrol

1

Alle bisherigen Lösungen verwenden Regexps, keine Zeichenfolgen, und werden daher fehlschlagen, wenn Ihre Zeichenfolgen RE-Metazeichen enthalten. Dies ist, wie zu tun, was Sie mit Streichern wollen, wie Sie in Ihrer Frage gestellt:

$ awk ' 
BEGIN { 
    begStr = "Iteration 1" 
    endStr = "Iteration 2" 
} 
index($0,begStr) == 1 + length($0) - length(begStr) { begNr = NR } 
index($0,endStr) == 1 + length($0) - length(endStr) { print NR - begNr - 1 } 
' file 
1 
+0

Ich glaube zwar nicht, dass das Übergeben willkürlicher Strings in den Bereich der Frage fiel (bei ungezwungener Formulierung), ++ für eine allgemeinere Lösung. Ziehen Sie zum Optimieren in Erwägung, 'next' an die erste Aktion anzuhängen und' exit' an die zweite. – mklement0

+0

Um den Vorteil Ihres Ansatzes besser zu veranschaulichen, schlage ich vor, die Strings _vie Variablen von außen zu übergeben_: awk -v begStr = 'Iteration 1' -v endStr = 'Iteration 2' '...' ' – mklement0

+1

Ich habe das nicht gemacht, weil ich dann Backslash-Erweiterung und IDK erklären/behandeln müsste, wenn das OP eine solche Lösung benötigt. Ich möchte auch keine Zeit damit verschwenden, sie zu optimieren, da das OP sie wahrscheinlich sowieso nicht benutzen wird. Ich fand es einfach wichtig, dass irgendjemand diese Frage in der Zukunft antrifft, um zu sehen, wie man wirklich Strings anstelle von Regexps verwendet. –

Verwandte Themen