Kan grep bare vise spesifiserte grupperinger som samsvarer?

La oss si at jeg har en fil:

# file: 'test.txt'
foobar bash 1
bash
foobar happy
foobar

Jeg vil bare vite hvilke ord som vises etter "foobar", slik at jeg kan bruke denne regexen:

"foobar \(\w\+\)"

Parentesen indikerer at jeg er spesielt interessert i ordet rett etter foobar. Men når jeg gjør en grep "foobar \(\w\+\)" test.txt, får jeg hele linjene som matcher hele regexen, i stedet for bare "ordet etter foobar":

foobar bash 1
foobar happy

Jeg ville foretrekke at resultatet av kommandoen så slik ut:

bash
happy

Finnes det en måte å be grep om å bare sende ut de elementene som samsvarer med grupperingen (eller en bestemt gruppering) i et regulært uttrykk?

Løsning

GNU grep har alternativet -P for regexer i perl-stil, og alternativet -o for å skrive ut bare det som samsvarer med mønsteret. Disse kan kombineres ved hjelp av look-around assertions (beskrevet under [Extended Patterns in the perlre manpage][1]) for å fjerne en del av grep-mønsteret fra det som er bestemt å ha matchet i forbindelse med -o.

$ grep -oP 'foobar \K\w+' test.txt
bash
happy
$

\K er kortformen (og en mer effektiv form) av `(?

Kommentarer (8)

Standard grep kan ikke gjøre dette, men nyere versjoner av GNU grep kan. Du kan bruke sed, awk eller perl. Her er noen eksempler som gjør det du ønsker på din eksempelinput; de oppfører seg litt forskjellig i hjørnetilfeller.

Erstatt foobar word other stuff med word, skriv bare ut hvis en erstatning er gjort.

sed -n -e 's/^foobar \([[:alnum:]]\+\).*/\1/p'

Hvis det første ordet er foobar, skriv ut det andre ordet.

awk '$1 == "foobar" {print $2}'

Fjern foobar hvis det er det første ordet, og hopp over linjen ellers; fjern deretter alt etter det første mellomrommet og skriv ut.

perl -lne 's/^foobar\s+// or next; s/\s.*//; print'
Kommentarer (5)

Hvis du vet at foobar alltid er det første ordet eller den første linjen, kan du bruke cut. På denne måten:

grep "foobar" test.file | cut -d" " -f2
Kommentarer (2)