Jak wydrukować dopasowany wzorzec wyrażenia regularnego za pomocą awk?

109

Używając awk, muszę znaleźć słowo w pliku, które pasuje do wzorca regex.

Chcę tylko wydrukować słowo dopasowane do wzoru.

Więc jeśli w linii mam:

xxx yyy zzz

I wzór:

/yyy/

Chcę tylko:

yyy

EDYCJA: dzięki kurumi udało mi się napisać coś takiego:

awk '{
        for(i=1; i<=NF; i++) {
                tmp=match($i, /[0-9]..?.?[^A-Za-z0-9]/)
                if(tmp) {
                        print $i
                }
        }
}' $1

i właśnie tego potrzebowałem :) wielkie dzięki!

marverix
źródło
1
@maxtaldykin Czy mógłbyś przenieść swoją odpowiedź własną z pytania do osobnej odpowiedzi?
kenorb
2
Nie musisz tego robić tmp=match($i, /regexp);if(tmp){}, po prostu powinieneś być w stanie to zrobić, if(tmp ~ $i){}ponieważ ~oznacza „pasuje do wyrażenia regularnego”.
JustinCB

Odpowiedzi:

148

To jest bardzo podstawowe

awk '/pattern/{ print $0 }' file

poproś awko wyszukanie patternużycia //, a następnie wydrukuj wiersz, który domyślnie nazywa się rekordem, oznaczony przez $ 0. Przeczytaj przynajmniej dokumentację .

Jeśli chcesz tylko uzyskać, wydrukuj dopasowane słowo.

awk '{for(i=1;i<=NF;i++){ if($i=="yyy"){print $i} } }' file
kurumi
źródło
49
Ponieważ printjest to akcja domyślna: awk '/pattern/' filewystarczy.
Johnsyweb
18
@Johnsyweb, tak, znam ten fakt. Dla początkujących, takich jak Marverix, ma być bardziej wizualny.
kurumi,
21
Nie wątpię w twoją wiedzę. Informacje te mogą być jednak przydatne dla innych, którzy znajdą tę odpowiedź.
Johnsyweb
2
Uwaga: @marverix będzie wymagało trochę więcej pracy domowej, aby for-loop działał, jeśli (a) „yyy” jest wyrażeniem regularnym, a nie prostym ciągiem, oraz (b) jeśli „yyy” nie pasuje do całego pola w nagranie.
Johnsyweb
8
To nie byłoby $i=="yyy"; byłoby to $i ~ /yyy/dla wyrażenia regularnego.
JustinCB
118

Wygląda na to, że próbujesz naśladować grep -ozachowanie GNU . To zrobi to pod warunkiem, że chcesz tylko pierwszy mecz w każdej linii:

awk 'match($0, /regex/) {
    print substr($0, RSTART, RLENGTH)
}
' file

Oto przykład użycia awkimplementacji GNU ():

awk 'match($0, /a.t/) {
    print substr($0, RSTART, RLENGTH)
}
' /usr/share/dict/words | head
act
act
act
act
aft
ant
apt
art
art
art

Przeczytaj o match, substr, RSTARToraz RLENGTHw awkinstrukcji.

Następnie możesz chcieć rozszerzyć to, aby poradzić sobie z wieloma dopasowaniami w tej samej linii.

Johnsyweb
źródło
NB: Aby odpowiedzieć na tę ostatnią część, wszystkie potrzebne konstrukty są w odpowiedzi Kurumi i mojej własnej.
Johnsyweb
Świetna odpowiedź. Chciałbym tutaj wyjaśnić, ponieważ jestem leniwy. Ale właśnie dlatego używam AWK!
lukas.pukenis
A jeśli chcę coś zrobić z wynikiem dopasowania, z wyjątkiem wydrukowania go? Na przykład chcę dodać wszystkie dopasowania do tablicy.
Evya 2005
@ evya2005: Możesz po prostu zastąpić wywołanie Ron print przydziałem, którego potrzebujesz.
Johnsyweb
to nie działa dla mnie. tylko drukuj prace. czy możesz mi pokazać przykład?
Evya 2005
36

gawk może pobrać pasującą część każdej linii, używając tego jako akcji:

{ if (match($0,/your regexp/,m)) print m[0] }

match (string, regexp [, array]) Jeśli tablica jest obecna, jest czyszczona, a następnie zerowy element tablicy jest ustawiany na całą część ciągu dopasowaną przez wyrażenie regularne. Jeśli wyrażenie regularne zawiera nawiasy, elementy tablicy indeksowane liczbami całkowitymi są ustawiane tak, aby zawierały część ciągu pasującą do odpowiedniego wyrażenia podrzędnego w nawiasach. http://www.gnu.org/software/gawk/manual/gawk.html#String-Functions

Royas
źródło
13

Jeśli interesuje Cię tylko ostatnia linia danych wejściowych i spodziewasz się znaleźć tylko jedno dopasowanie (na przykład część linii podsumowania polecenia powłoki), możesz również wypróbować ten bardzo zwarty kod, przyjęty z Jak drukować dopasowania wyrażeń regularnych używając „awk”? :

$ echo "xxx yyy zzz" | awk '{match($0,"yyy",a)}END{print a[0]}'
yyy

Lub bardziej złożona wersja z częściowym wynikiem:

$ echo "xxx=a yyy=b zzz=c" | awk '{match($0,"yyy=([^ ]+)",a)}END{print a[1]}'
b

Ostrzeżenie: awk match()funkcja z trzema argumentami istnieje tylko w gawk, nie wmawk

Oto kolejne fajne rozwiązanie wykorzystujące lookbehind regex w grepzamiast awk. To rozwiązanie ma mniejsze wymagania dotyczące instalacji:

$ echo "xxx=a yyy=b zzz=c" | grep -Po '(?<=yyy=)[^ ]+'
b
Daniel Alder
źródło
Dlaczego dodałeś "tail -n1"? To powinno działać dobrze bez tego, prawda?
Arthur Accioly
1
@ArthurAccioly Poprawne. Użyłem tego terminu, aby wyodrębnić średni czas podróży w obie strony z połączenia ping, stąd pochodzi. zabawne, że odkrycie go zajęło 4 lata;)
Daniel Alder
12

Jeśli Perl jest opcją, możesz spróbować tego:

perl -lne 'print $1 if /(regex)/' file

Aby zaimplementować dopasowanie bez rozróżniania wielkości liter, dodaj imodyfikator

perl -lne 'print $1 if /(regex)/i' file

Aby wydrukować wszystko PO meczu:

perl -lne 'if ($found){print} else{if (/regex(.*)/){print $1; $found++}}' textfile

Aby wydrukować dopasowanie i wszystko po dopasowaniu:

perl -lne 'if ($found){print} else{if (/(regex.*)/){print $1; $found++}}' textfile
Chris Koknat
źródło
3

Używanie seda również może być eleganckie w tej sytuacji. Przykład (zamień wiersz na dopasowaną grupę „yyy” z wiersza):

$ cat testfile
xxx yyy zzz
yyy xxx zzz
$ cat testfile | sed -r 's#^.*(yyy).*$#\1#g'
yyy
yyy

Odpowiednia strona podręcznika: https://www.gnu.org/software/sed/manual/sed.html#Back_002dreferences-and-Subexpressions

Konrad Brodzik
źródło
Dla seda innego niż gnu rozwiązanie jest takie:sed -n 's/^.*\(yyy\).*$/\1/gp' < testfile
Grigory Entin,
1
@GrigoryEntin - bsd sed działa dobrze z oryginalną odpowiedzią. Rozszerzony przełącznik regex obsługiwany przez POSIX to -E, ale we FreeBSD przynajmniej -r jest tym samym, co -E (-r dodane w 2010). W każdym razie spróbuj z -E (gnu sed dodano -E w 4.3)
Juan
3

Poza tematem, można to zrobić również za pomocą grepa, po prostu umieszczając go tutaj na wypadek, gdyby ktoś szukał rozwiązania grep

echo 'xxx yyy zzze ' | grep -oE 'yyy'
Zeus
źródło
Prosty sposób na złapanie go nawet przy użyciu wyrażenia regularnego. Dokładnie to, czego potrzebowałem. Dzięki!
Marquee
To działa dla mnie; Mój przypadek jest taki: echo "web_port = 8080, shutdown_port = 8005" | grep -oE "web_port = [0-9] +" # return 8080
Robb Tsang
0

Jeśli wiesz, w której kolumnie znajduje się tekst / wzorzec, którego szukasz (np. „Yyy”), możesz po prostu sprawdzić tę konkretną kolumnę, aby zobaczyć, czy pasuje, i wydrukować.

Na przykład mając plik z następującą zawartością (nazywany asdf.txt )

xxx yyy zzz

aby wydrukować tylko drugą kolumnę, jeśli pasuje ona do wzorca „yyy”, możesz zrobić coś takiego:

awk '$2 ~ /yyy/ {print $2}' asdf.txt

Zwróć uwagę, że będzie to również pasować do każdego wiersza, w którym druga kolumna zawiera „yyy”, na przykład:

xxx yyyz zzz
xxx zyyyz
kimbo
źródło