Jak zaznaczyć linie między dwoma wzorami znaczników, które mogą wystąpić wielokrotnie w awk / sed


119

Za pomocą awklub sedjak mogę wybrać linie, które występują między dwoma różnymi wzorami znaczników? Może być wiele sekcji oznaczonych tymi wzorami.

Na przykład: załóżmy, że plik zawiera:

abc
def1
ghi1
jkl1
mno
abc
def2
ghi2
jkl2
mno
pqr
stu

Wzorzec początkowy to, abca wzór końcowy to mno Więc potrzebuję wyniku jako:

def1
ghi1
jkl1
def2
ghi2
jkl2

Używam seda do dopasowania wzorca raz:

sed -e '1,/abc/d' -e '/mno/,$d' <FILE>

Czy istnieje jakiś sposób sedczy awk to zrobić, aż do końca pliku?

Odpowiedzi:


188

Użyj awkz flagą, aby wywołać drukowanie w razie potrzeby:

$ awk '/abc/{flag=1;next}/mno/{flag=0}flag' file
def1
ghi1
jkl1
def2
ghi2
jkl2

Jak to działa?

  • /abc/dopasowuje także wiersze zawierające ten tekst /mno/.
  • /abc/{flag=1;next}ustawia flagmoment abcznalezienia tekstu . Następnie pomija linię.
  • /mno/{flag=0}anuluje, flaggdy tekst mnozostanie znaleziony.
  • Ostatnim flagjest wzorzec z domyślną akcją, która jest następująca print $0: jeśli flagjest równe 1, drukowany jest wiersz.

Aby uzyskać bardziej szczegółowy opis i przykłady, a także przypadki, w których wzory są wyświetlane lub nie, zobacz Jak wybrać linie między dwoma wzorami? .


30
Jeśli chcesz wydrukować wszystko pomiędzy wzorem włącznie , możesz użyć awk '/abc/{a=1}/mno/{print;a=0}a' file.
scai

6
Tak, @scai! lub nawet awk '/abc/{a=1} a; /mno/{a=0}' file- z tym, umieszczając awarunek przed parametrem, /mno/sprawimy, że oceni wiersz jako prawdziwy (i wydrukujemy go) przed ustawieniem a=0. W ten sposób możemy uniknąć pisania print.
fedorqui 'SO przestać szkodzić'

12
@scai @fedorqui Aby uwzględnić wzór wyjściowy, możesz to zrobićawk '/abc/,/mno/' file
Jotne

1
@hkasera awk '/abc/{flag=1}/mno/{flag=0}flag' filepowinien zrobić.
fedorqui 'SO przestać krzywdzić'

2
@EirNym to dziwny scenariusz, który można obsłużyć na bardzo różne sposoby: które wiersze chcesz wydrukować? Prawdopodobnie awk 'flag; /PAT1/{flag=1; next} /PAT1/{flag=0}' filezrobi.
fedorqui 'SO przestać krzywdzić'

45

Używając sed:

sed -n -e '/^abc$/,/^mno$/{ /^abc$/d; /^mno$/d; p; }'

Te -nśrodki nie są drukowane opcja domyślnie.

Wzorzec szuka wierszy zawierających tylko abcto just mno, a następnie wykonuje akcje w { ... }. Pierwsza akcja usuwa abcwiersz; druga mnolinia; a pdrukuje pozostałe wiersze. W razie potrzeby możesz rozluźnić wyrażenia regularne. Żadne wiersze spoza zakresu abc... mnopo prostu nie są drukowane.


Dziękuję za odpowiedź i wyjaśnienie! :)
dvai

@JonathanLeffler czy mogę wiedzieć, jaki jest cel używania-e
Kasun Siyambalapitiya

1
@KasunSiyambalapitiya: Przede wszystkim oznacza to, że lubię go używać. Formalnie określa, że ​​następny argument jest (częścią) skryptu, który sedpowinien zostać wykonany. Jeśli chcesz lub potrzebujesz użyć kilku argumentów, aby uwzględnić cały skrypt, musisz użyć ich -eprzed każdym takim argumentem; w przeciwnym razie jest opcjonalne (ale jawne).
Jonathan Leffler

@JonathanLeffler Thanks
Kasun Siyambalapitiya

Miły! (Wolę sed od awk.) Gdy używamy złożonych wyrażeń regularnych, byłoby miło nie musieć ich powtarzać. Czy nie można usunąć pierwszej / ostatniej linii z „wybranego” zakresu? Czy też najpierw zastosować ddo wszystkich linii do pierwszego dopasowania, a następnie ddo wszystkich linii zaczynających się od drugiego dopasowania?
hans_meine,

18

To może zadziałać dla Ciebie (GNU sed):

sed '/^abc$/,/^mno$/{//!b};d' file

Usuń wszystkie wiersze oprócz tych między wierszami rozpoczynającymi się abcimno



To jest niesamowite. W {//!b}zapobiega abci mnood włączenia do wyjścia, ale nie mogę dowiedzieć się, jak to zrobić. Czy możesz wytłumaczyć?
Brendan

1
@Brendan instrukcja //!bczyta, jeśli bieżąca linia nie jest jedną z linii, które pasują do zakresu, przerwij i dlatego wypisz te linie, w przeciwnym razie wszystkie inne linie zostaną usunięte.
potong

13
sed '/^abc$/,/^mno$/!d;//d' file

gra o dwie postacie lepiej niż ppotong {//!b};d

Puste ukośniki //oznaczają: „użyj ponownie ostatniego użytego wyrażenia regularnego”. a polecenie robi to samo, co bardziej zrozumiałe:

sed '/^abc$/,/^mno$/!d;/^abc$/d;/^mno$/d' file

To wydaje się być POSIX :

Jeśli jednostka RE jest pusta (to znaczy nie określono żadnego wzorca), sed zachowuje się tak, jakby ostatnia jednostka RE użyta w ostatnim zastosowanym poleceniu (jako adres lub część polecenia zastępczego) została określona.


1
Myślę, że drugie rozwiązanie zakończy się niczym, ponieważ drugie polecenie to również zakres. Jednak uznanie za pierwsze.
potong

@potong true! Muszę się więcej dowiedzieć, dlaczego ten pierwszy działa. Dzięki!
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

7

Z linków z poprzedniej odpowiedzi wynika, że ​​ten, który zrobił to dla mnie, działając kshna Solarisie, był taki:

sed '1,/firstmatch/d;/secondmatch/,$d'
  • 1,/firstmatch/d: od linii 1 do pierwszego znalezienia firstmatch , usuń.
  • /secondmatch/,$d: od pierwszego wystąpienia secondmatch do końca pliku, usuń.
  • Średnik oddziela dwa polecenia, które są wykonywane po kolei.

Ciekawe, dlaczego ogranicznik zakresu ( 1,) występuje wcześniej /firstmatch/? Zgaduję, że można to również wyrazić '/firstmatch/1,d;/secondmatch,$d'?
Luke Davis,

2
Z „1, / firstmatch / d” mówisz „od linii 1 do pierwszego znalezienia„ firstmatch ”, usuń”. Natomiast przy „/ secondmatch /, $ d” mówisz „od pierwszego wystąpienia„ secondmatch ”do końca pliku, usuń”. średnik oddziela dwa polecenia, które są wykonywane po kolei.
FanDeLaU

2
perl -lne 'print if((/abc/../mno/) && !(/abc/||/mno/))' your_file

Dobrze wiedzieć odpowiednik Perla, ponieważ jest to całkiem dobra alternatywa zarówno dla awk, jak i sed.
akhan

2

coś takiego działa u mnie:

file.awk:

BEGIN {
    record=0
}

/^abc$/ {
    record=1
}

/^mno$/ {
    record=0;
    print "s="s;
    s=""
}

!/^abc|mno$/ {
    if (record==1) {
        s = s"\n"$0
    }   
}

za pomocą: awk -f file.awk data ...

edit: O_o fedorqui rozwiązanie jest o wiele lepsze / ładniejsze niż moje.


3
W GNU awk if (record=1)powinno być if (record==1), tj. Podwójne = - patrz operatory porównania gawk
George Hawkins

2

Odpowiedź Don_crissti z Pokaż tylko tekst między 2 pasującymi wzorami ?

firstmatch="abc"
secondmatch="cdf"
sed "/$firstmatch/,/$secondmatch/!d;//d" infile

który jest znacznie bardziej wydajny niż aplikacja AWK, patrz tutaj .


Myślę, że łączenie porównań czasowych nie ma tu większego sensu, ponieważ wymagania pytań są zupełnie inne, stąd rozwiązania.
fedorqui 'SO przestań szkodzić'

2
Nie zgadzam się, ponieważ powinniśmy mieć pewne kryteria porównywania odpowiedzi. Tylko kilka ma aplikacje SED.
Léo Léopold Hertz 준영

0

Próbowałem użyć awkdo wydrukowania linii między dwoma wzorami, podczas gdy wzorzec 2 również pasuje do wzorca 1 . Należy również wydrukować linię pattern1.

np. źródło

package AAA
aaa
bbb
ccc
package BBB
ddd
eee
package CCC
fff
ggg
hhh
iii
package DDD
jjj

powinien mieć ouput

package BBB
ddd
eee

Gdzie package BBBwzorzec1 jest, tam wzorzec2 package \w*. Zauważ, że CCCnie jest to znana wartość, więc nie można jej dosłownie dopasować.

W tym przypadku ani @scai, ani @fedorqui awk '/abc/{a=1}/mno/{print;a=0}a' filenie awk '/abc/{a=1} a; /mno/{a=0}' filedziałają dla mnie.

W końcu udało mi się to rozwiązać awk '/package BBB/{flag=1;print;next}/package \w*/{flag=0}flag' file, haha

Trochę więcej wysiłku powoduje awk '/package BBB/{flag=1;print;next}flag;/package \w*/{flag=0}' filewydrukowanie również linii wzorca2, to znaczy

package BBB
ddd
eee
package CCC
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.