Как распечатать согласованный шаблон регулярного выражения с помощью awk?


110

Используя awk, мне нужно найти слово в файле, которое соответствует шаблону регулярного выражения.

Я только хочу , чтобы напечатать слово совпадающего с шаблоном.

Итак, если в строке у меня есть:

xxx yyy zzz

И узор:

/yyy/

Я хочу получить только:

yyy

РЕДАКТИРОВАТЬ: благодаря Куруми мне удалось написать что-то вроде этого:

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

и это то, что мне было нужно :) большое спасибо!


1
@maxtaldykin Не могли бы вы переместить свой самостоятельный ответ из вопроса в отдельный ответ?
kenorb

2
Вам не нужно делать это tmp=match($i, /regexp);if(tmp){}, вы просто должны уметь это делать, if(tmp ~ $i){}потому что ~означает «соответствует регулярному выражению».
JustinCB

Ответы:


148

Это самый простой

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

попросите awkнайти patternиспользование //, затем распечатайте строку, которая по умолчанию называется записью и обозначается $ 0. По крайней мере, прочтите документацию .

Если вы хотите только распечатать совпадающее слово.

awk '{for(i=1;i<=NF;i++){ if($i=="yyy"){print $i} } }' file

50
Так printкак действие по умолчанию: awk '/pattern/' fileбудет достаточно.
Johnsyweb

18
@Johnsyweb, да, я знаю этот факт. Для новичка, такого как Marverix, он должен быть более наглядным.
Куруми

21
Я не сомневаюсь в твоих знаниях. Однако информация может быть полезной для других, нашедших этот ответ.
Johnsyweb

2
NB: @marverix потребуется немного больше домашней работы, чтобы заставить for-loop работать, если (a) «yyy» является регулярным выражением, а не прямой строкой и (b) если это «yyy» не соответствует всему полю внутри запись.
Johnsyweb

8
Этого не было бы $i=="yyy"; это было бы $i ~ /yyy/для регулярного выражения.
JustinCB

118

Похоже, вы пытаетесь подражать grep -oповедению GNU . Это будет сделано, если вам нужно только первое совпадение в каждой строке:

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

Вот пример использования awkреализации 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

Читайте о match, substr, RSTARTи RLENGTHв awkручном.

После этого вы можете захотеть расширить это, чтобы иметь дело с несколькими совпадениями в одной строке.


NB: Чтобы ответить на эту последнюю часть, все необходимые конструкции есть в ответе Куруми и в моем собственном.
Johnsyweb

Отличный ответ. Просто я хотел бы получить здесь объяснение, потому что я ленив. Но именно поэтому я использую AWK!
lukas.pukenis

Что, если я хочу что-то сделать с результатом совпадения, кроме его печати? Например, я хочу добавить все совпадения в массив.
Evya2005 04

@ evya2005: Вы можете просто заменить call Ron print нужным вам заданием.
Johnsyweb

у меня это не работает. только печатная работа. можешь показать мне пример?
Evya2005 04

36

gawk может получить соответствующую часть каждой строки, используя это как действие:

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

match (string, regexp [, array]) Если массив присутствует, он очищается, а затем нулевым элементом массива устанавливается вся часть строки, соответствующая регулярному выражению. Если регулярное выражение содержит круглые скобки, элементы массива с целочисленным индексом устанавливаются так, чтобы содержать часть строки, соответствующую соответствующему подвыражению в скобках. http://www.gnu.org/software/gawk/manual/gawk.html#String-Functions


13

Если вас интересует только последняя строка ввода и вы ожидаете найти только одно совпадение (например, часть итоговой строки команды оболочки), вы также можете попробовать этот очень компактный код, заимствованный из Как печатать совпадения регулярных выражений используя awk? :

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

Или более сложная версия с частичным результатом:

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

Предупреждение: awk match()функция с тремя аргументами существует только в gawk, а не вmawk

Вот еще хорошее решение с использованием ' назад регулярное выражение в grepвместо awk. Это решение предъявляет более низкие требования к вашей установке:

$ echo "xxx=a yyy=b zzz=c" | grep -Po '(?<=yyy=)[^ ]+'
b

Почему вы добавили "tail -n1"? Это должно нормально работать и без него, не так ли?
Arthur Accioly

1
@ArthurAccioly Верно. Я использовал этот термин для извлечения среднего времени приема-передачи из пинга, вот откуда оно взялось. забавно, что на то, чтобы это открыть, потребовалось 4 года;)
Дэниел Алдер

12

Если Perl - вариант, вы можете попробовать следующее:

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

Чтобы реализовать сопоставление без учета регистра, добавьте iмодификатор

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

Чтобы распечатать все ПОСЛЕ матча:

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

Чтобы распечатать совпадение и все, что после него:

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

3

Использование sed также может быть элегантным в этой ситуации. Пример (заменить строку соответствующей группой «yyy» из строки):

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

Соответствующая страница руководства: https://www.gnu.org/software/sed/manual/sed.html#Back_002dreferences-and-Subexpressions


Для non-gnu sed решение выглядит примерно так:sed -n 's/^.*\(yyy\).*$/\1/gp' < testfile
Григорий Энтин

1
@GrigoryEntin - bsd sed отлично работает с исходным ответом. Переключатель расширенного регулярного выражения, поддерживаемый POSIX, - -E, но во FreeBSD по крайней мере -r совпадает с -E (-r добавлено в 2010 году). В любом случае, попробуйте с -E (gnu sed добавил -E в 4.3)
Хуан

3

Не по теме, это также можно сделать с помощью grep, просто разместив его здесь, если кто-то ищет решение grep

echo 'xxx yyy zzze ' | grep -oE 'yyy'

Простой способ получить его даже с помощью регулярного выражения. Именно то, что мне нужно. Спасибо!
Marquee,

У меня это работает; Мой случай такой: echo "web_port = 8080, shutdown_port = 8005" | grep -oE "web_port = [0-9] +" # return 8080
Робб Цанг,

0

Если вы знаете, в каком столбце находится текст / шаблон, который вы ищете (например, «yyy»), вы можете просто проверить этот конкретный столбец, чтобы увидеть, соответствует ли он, и распечатать его.

Например, учитывая файл со следующим содержимым (называемый asdf.txt )

xxx yyy zzz

чтобы напечатать второй столбец только в том случае, если он соответствует шаблону «yyy», вы можете сделать что-то вроде этого:

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

Обратите внимание, что это также будет соответствовать практически любой строке, где во втором столбце есть «yyy», например:

xxx yyyz zzz
xxx zyyyz
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.