Übereinstimmende Zeile und n-te Zeile aus der übereinstimmenden Zeile drucken

18

Ich versuche, die übereinstimmende Zeile und die vierte Zeile aus der übereinstimmenden Zeile (Zeile mit dem gesuchten Ausdruck) zu drucken.

Ich habe den folgenden Code verwendet: sed -n 's/^[ \t]*//; /img class=\"devil_icon/,4p' input.txt

Es wird jedoch nur die übereinstimmende Zeile gedruckt.

Dies druckt nur die 4. Zeile. awk 'c&&!--c;/img class=\"devil_icon/{c=4}' input.txt

Ich muss nur die übereinstimmende Zeile und die vierte Zeile drucken.

debal
quelle
Verwenden Sie egrep "pattern" -A4
Valentin Bajrami
@ val0x00ff, das auch die Zeilen dazwischen druckt. Das heißt: Es werden die nächsten 4 Zeilen gedruckt, beginnend mit der übereinstimmenden Zeile
Debal
Sie sagen: "Ich versuche, die übereinstimmende Zeile und die vierte Zeile aus der übereinstimmenden Zeile zu drucken." Dies grep -A 4 "pattern" file | sed -n '4p'macht genau das, was Sie wollen, es sei denn, ich missverstehe Sie
Valentin Bajrami
Nein, tut es nicht. Die Ausgabe des obigen Codes war </td>nicht die 4. Zeile
Debal 24.08.13

Antworten:

18

In awk würden Sie das wie folgt machen

awk '/pattern/{nr[NR]; nr[NR+4]}; NR in nr' file > new_file`

oder

awk '/pattern/{print; nr[NR+4]; next}; NR in nr' file > new_file`

Erläuterung

Die erste Lösung findet alle übereinstimmenden Zeilen pattern. Wenn eine Übereinstimmung gefunden wird, wird die Datensatznummer ( NR) im Array gespeichert nr. Es speichert auch den 4. Datensatz NRin demselben Array. Dies geschieht durch die nr[NR+4]. Jeder Datensatz ( NR) wird dann überprüft, um festzustellen, ob er im nrArray vorhanden ist. In diesem Fall wird der Datensatz gedruckt.

Die zweite Lösung funktioniert im Wesentlichen genauso, außer wenn sie auf patterndiese Zeile stößt und dann den vierten Datensatz davor im Array speichert und dann nrzum nächsten Datensatz wechselt. Wenn Sie dann awkauf diesen 4. Satz stoßen, wird der NR in nrBlock ausgeführt und gibt diesen +4 Satz danach aus.

Beispiel

Hier ist ein Beispiel für eine Datendatei sample.txt.

$ cat sample.txt 
1
2
3
4 blah
5
6
7
8
9
10 blah
11
12
13
14
15
16

Verwendung der 1. Lösung:

$ awk '/blah/{nr[NR]; nr[NR+4]}; NR in nr' sample.txt 
4 blah
8
10 blah
14

Verwendung der 2. Lösung:

$ awk '/blah/{print; nr[NR+4]; next}; NR in nr' sample.txt 
4 blah
8
10 blah
14
Valentin Bajrami
quelle
3
Schön, +1. Sie verwenden hier viele awkVerknüpfungen. Können Sie eine kurze Erklärung hinzufügen (z. B., dass Print in awk impliziert ist und Arrays assoziativ usw. sind)?
Terdon
Wenn Sie mit @terdon einverstanden sind, können Sie den Code etwas erläutern.
Debal
@slm Danke für die Verbesserung und die vollständige Beantwortung!
Valentin Bajrami
1
Danke für die Antwort, ich habe auch etwas Neues damit gelernt.
SLM
4
sed -n 's/^[ \t]*/; /img class=\"devil_icon/,+4 { 3,5d ; p }' input.txt

Ich füge einfach vor dem Drucken eine Löschung der entsprechenden Zeilen hinzu { 3,5d ; p }.

Drav Sloan
quelle
Ihr Ausdruck erzeugt einen Fehler: sed: -e expression #1, char 18: unknown option to s'`
Mineralien
4

Sie können es mit der -AOption versuchen grep, die angibt, wie viele Zeilen nach der übereinstimmenden Zeile gedruckt werden sollen. Wenn Sie dies mit koppeln sed, erhalten Sie die erforderlichen Zeilen.

grep -A 4 pattern input.txt | sed -e '2,4d'

Mit sedlöschen wir die von der zweiten bis zur vierten Zeile.

Barun
quelle
3
Dies setzt eine einzelne Übereinstimmung patternin der Datei voraus .
Terdon
2

Hier ist ein Weg in Perl, der mit einer beliebigen Anzahl übereinstimmender Zeilen umgehen kann:

perl -ne '/pattern/ && do{$c=$.; print}; $.==$c+4 && print' file > new_file`

In Perl. Die spezielle Variable $.ist die aktuelle Zeilennummer. Jedes Mal, wenn ich eine passende Zeile finde, drucke patternich sie aus und speichere ihre Zeilennummer unter $c. Ich drucke dann erneut, wenn die aktuelle Zeilennummer 4 mehr ist als die zuvor gedruckte.

terdon
quelle
0
awk 'c&&!--c;/img class=\"devil_icon/{c=4};/img class=\"devil_icon/' input.txt

Sie machen im Wesentlichen ein Suchen und Ersetzen. Sie können demselben Befehl nur einen Fund hinzufügen , der beide ausgibt :)

awk 'c&&!--c;/pattern/{c=4};/pattern/' input.txt
bacoNx1000
quelle