Drukuj pasującą linię i n-tą linię z dopasowanej linii

18

Próbuję wydrukować dopasowaną linię i czwartą linię z dopasowanej linii (linia zawierająca szukane wyrażenie).

Korzystałem z następującego kodu: sed -n 's/^[ \t]*//; /img class=\"devil_icon/,4p' input.txt

Ale to tylko drukuje dopasowaną linię.

Drukuje tylko czwartą linię. awk 'c&&!--c;/img class=\"devil_icon/{c=4}' input.txt

Muszę wydrukować tylko dopasowaną linię i tylko czwartą linię.

debal
źródło
Użyj egrep "pattern" -A4
Valentin Bajrami
@ val0x00ff, który drukuje również linie między nimi .. to znaczy: drukuje kolejne 4 linie, zaczynając od dopasowanej linii
debal
mówisz „Próbuję wydrukować dopasowaną linię i czwartą linię z dopasowanej linii”. To grep -A 4 "pattern" file | sed -n '4p'robi dokładnie to, co chcesz, chyba że ja cię nieporozumienie
Valentin Bajrami
nie to nie. Wyjście powyższego kodu </td>nie było czwartym wierszem
debata

Odpowiedzi:

18

W awk zrobiłbyś to w następujący sposób

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

lub

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

Wyjaśnienie

Pierwsze rozwiązanie wyszukuje wszystkie pasujące linie pattern. Po znalezieniu dopasowania zapisuje numer rekordu ( NR) w tablicy nr. Przechowuje również 4. rekord z NRtej samej tablicy. Odbywa się to przez nr[NR+4]. Każdy rekord ( NR) jest następnie sprawdzany, aby sprawdzić, czy jest obecny w nrtablicy, a jeśli tak, to rekord jest drukowany.

Drugie rozwiązanie działa zasadniczo w ten sam sposób, z wyjątkiem sytuacji, gdy napotka patterndrukowaną linię, a następnie zapisze 4. rekord przed nią w tablicy nr, a następnie przejdzie do następnego rekordu. Następnie, gdy awknapotka ten czwarty rekord, NR in nrblok zostanie wykonany i wydrukuje ten rekord +4 po nim.

Przykład

Poniżej znajduje się plik danych przykładem sample.txt.

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

Korzystanie z pierwszego rozwiązania:

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

Korzystanie z drugiego rozwiązania:

$ awk '/blah/{print; nr[NR+4]; next}; NR in nr' sample.txt 
4 blah
8
10 blah
14
Valentin Bajrami
źródło
3
Fajnie, +1. Używasz tutaj wielu awkskrótów, czy mógłbyś dodać krótkie wyjaśnienie (rzeczy takie jak druk sugerowany w awk i że tablice są skojarzone itp.)?
terdon
zgadzam się z @terdon, proszę mógłbyś trochę wyjaśnić kod.
debal
@slm Dziękujemy za ulepszenie i udzielenie pełnej odpowiedzi!
Valentin Bajrami
1
Dzięki za odpowiedź, nauczyłem się również czegoś nowego.
slm
4
sed -n 's/^[ \t]*/; /img class=\"devil_icon/,+4 { 3,5d ; p }' input.txt

Po prostu dodałem usunięcie odpowiednich linii przed drukowaniem { 3,5d ; p }.

Drav Sloan
źródło
twoje wyrażenie powoduje błąd: sed: -e expression #1, char 18: unknown option to s''
minerały
4

Możesz wypróbować -Aopcję grep, która określa, ile wierszy po dopasowanej linii powinno zostać wydrukowanych. Połącz to z sed, a otrzymasz wymagane linie.

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

Używając sed, usuwamy z drugiej linii do czwartej.

Barun
źródło
3
Zakłada to pojedyncze dopasowanie patternw pliku.
terdon
2

Oto sposób w Perlu, który może poradzić sobie z dowolną liczbą pasujących linii:

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

W Perlu. zmienna specjalna $.to bieżący numer linii. Za każdym razem, gdy znajduję pasującą linię pattern, drukuję ją i zapisuję jej numer jako $c. Następnie drukuję ponownie, gdy bieżący numer linii jest o 4 większy niż poprzednio wydrukowany.

terdon
źródło
0
awk 'c&&!--c;/img class=\"devil_icon/{c=4};/img class=\"devil_icon/' input.txt

Zasadniczo dokonujesz wyszukiwania i zastępowania. Możesz dodać po prostu znalezisko do tego samego polecenia i wydrukuje je oba :)

awk 'c&&!--c;/pattern/{c=4};/pattern/' input.txt
bacoNx1000
źródło