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!


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

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.


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


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

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

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


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'

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
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.