AWK: доступ к захваченной группе по шаблону линии

229

Если у меня есть команда awk

pattern { ... }

и шаблон использует группу захвата, как я могу получить доступ к строке, захваченной в блоке?

колокольчик-рапунцель
источник
Иногда (в простых случаях) можно настроить разделитель полей ( FS) и выбрать то, что вы хотите сопоставить с $field. Преформатирование ввода также может помочь.
Кшиштоф Яблонский
1
На дублирующийся вопрос есть лучший ответ .
Сэмюэль Эдвин Уорд
2
Сэмюэль Эдвин Уорд: Это тоже хороший ответ! Но это также требует gawk(так как он использует gensub).
Рэмпион

Ответы:

176

Это была прогулка по переулку памяти ...

Я заменил awk на perl очень давно.

Очевидно, механизм регулярных выражений AWK не захватывает его группы.

Вы можете рассмотреть возможность использования чего-то вроде:

perl -n -e'/test(\d+)/ && print $1'

флаг -n заставляет perl перебирать каждую строку, как это делает awk.

Питер Тиллеманс
источник
3
Видимо кто-то не согласен. Эта веб-страница с 2005 года: tek-tips.com/faqs.cfm?fid=5674 Это подтверждает, что вы не можете повторно использовать соответствующие группы в awk.
Питер Тиллеманс
3
Я предпочитаю «perl -n -p -e ...», а не awk для почти всех вариантов использования, так как он более гибкий, более мощный и, на мой взгляд, имеет более разумный синтаксис.
Питер Тиллеманс
15
gawk! = awk. Это разные инструменты и gawkпо умолчанию недоступны в большинстве мест.
Оли
6
ОП специально попросил решение awk, поэтому я не думаю, что это ответ.
Джоппе
6
@Joppe, вы не можете дать решение awk, если нет решения. В строке 3 я объясняю, что AWK не поддерживает группы захвата, и я дал альтернативу, которую ОП, очевидно, оценил, потому что этот ответ был принят. Как я мог лучше ответить на этот вопрос?
Питер Тиллеманс
335

С gawk вы можете использовать matchфункцию для захвата заключенных в скобки групп.

gawk 'match($0, pattern, ary) {print ary[1]}' 

пример:

echo "abcdef" | gawk 'match($0, /b(.*)e/, a) {print a[1]}' 

выходы cd.

Обратите внимание на конкретное использование gawk, которое реализует данную функцию.

Для портативной альтернативы вы можете достичь аналогичных результатов с match()и substr.

пример:

echo "abcdef" | awk 'match($0, /b[^e]*/) {print substr($0, RSTART+1, RLENGTH-1)}'

выходы cd.

Гленн Джекман
источник
4
Да, варианты gxxx обладают множеством дополнительных достоинств и мощности GNU.
Питер Тиллеманс
Работает и в BusyBox awk.
MrMas
32

Это то, что мне нужно все время, поэтому я создал для него функцию bash. Это основано на ответе Гленна Джекмана.

Определение

Добавьте это к вашему .bash_profile и т. Д.

function regex { gawk 'match($0,/'$1'/, ary) {print ary['${2:-'0'}']}'; }

использование

Записать регулярное выражение для каждой строки в файле

$ cat filename | regex '.*'

Захват 1-й группы захвата регулярных выражений для каждой строки в файле

$ cat filename | regex '(.*)' 1
opsb
источник
2
Чем он отличается от использования grep -o?
bfontaine
@bfontaine Может ли grep -oвыводить захваченные группы?
Olle Härstedt
1
@ OlleHärstedt Нет, не мог. Он охватывает только ваш вариант использования, когда у вас нет групп захвата. В этом случае это становится уродливым с цепями grep -o.
bfontaine
15

Вы можете использовать GNU awk:

$ cat hta
RewriteCond %{HTTP_HOST} !^www\.mysite\.net$
RewriteRule (.*) http://www.mysite.net/$1 [R=301,L]

$ gawk 'match($0, /.*(http.*?)\$/, m) { print m[1]; }' < hta
http://www.mysite.net/
Ишвара
источник
12
+1. Кроме того, с любым awk:awk 'match($0, /.*(http.*?)\$/) { print substr($0,RSTART,RLENGTH) }'
Эд Мортон
1
Эд Мортон: это заслуживает ответа на высшем уровне, я бы сказал. редактировать: хм ... это печатает RewriteRule (.*) http://www.mysite.net/$для меня, что больше, чем подгруппа.
Рэмпион
4

Вы также можете смоделировать захват в vanilla awk без расширений. Это не интуитивно, хотя:

шаг 1. используйте gensub, чтобы окружить совпадения каким-либо символом, который не появляется в вашей строке. Шаг 2. Используйте раскол против персонажа. Шаг 3. Каждый другой элемент в расщепленном массиве является вашей группой захвата.

$ echo 'ab cb ad' | awk '{split (gensub (/ a ./, SUBSEP "&" SUBSEP, "g", $ 0), cap, SUBSEP); крышка для печати [2] "|" колпачок [4]; }»
абы | объявления
ydrol
источник
3
Я почти уверен, что gensubэто gawkособая функция. Что вы получаете от вашего awk, если вы печатаете awk --version; -?). Всем удачи.
Shellter
6
Я полностью уверен, что gensub - это gawk-ism, хотя в BusyBox awk он также есть. Этот ответ также может быть реализован с использованием gsub, однако:echo 'ab cb ad' | awk '{gsub(/a./,SUBSEP"&"SUBSEP);split($0,cap,SUBSEP);print cap[2]"|"cap[4]}'
dubiousjim
3
gensub () - расширение gawk, в руководстве gawk об этом ясно сказано. Другие варианты awk могут также реализовать его, но это все еще не POSIX. Попробуйте gawk --posix '{gsub (...)}', и он будет жаловаться
MestreLion
2
@MestreLion, вы имеете в виду, что он будет жаловаться gawk --posix '{gensub(...)}'.
dubiousjim
1
Несмотря на то, что вы ошиблись в POSIX awk сgensub функцию, ваш пример был применен к очень ограниченному сценарию: весь шаблон сгруппирован, он не может соответствовать чему-то похожему на все, key=(value)когда я хочу извлечь только valueчасти.
мяу
2

Я немного изо всех сил пытался придумать функцию bash, которая заключает в себе ответ Питера Тиллемана, но вот что я придумал:

function regex {perl -n -e "/ $ 1 / && printf \"% s \ n \ "," '$ 1'}

Я обнаружил, что это работает лучше, чем основанная на awk функция opsb для следующего аргумента регулярного выражения, потому что я не хочу, чтобы выводился «ms».

'([0-9]*)ms$'
wytten
источник
Я предпочитаю это решение, так как вы можете видеть части группы, которые ограничивают захват, а также опускают их. Однако кто-то может объяснить, как это работает? Я не могу заставить этот синтаксис Perl работать должным образом в BASH, потому что я не очень хорошо понимаю его - особенно двойные / одинарные кавычки$1
Demis
Это не то, что я делал до или после этого, но, оглядываясь назад, он объединяет две строки, первая строка в двойных кавычках (эта первая строка содержит встроенные двойные кавычки, экранированные обратной косой чертой), а вторая строка в одинарных кавычках , Затем результат этой конкатенации передается в качестве аргумента в perl -e. Также вам нужно знать, что первый $ 1 (тот, что в двойных кавычках) заменяется первым аргументом функции, а второй $ 1 (тот, что в одинарных кавычках) остается нетронутым. Смотрите этот пример
Wytten
Понятно, теперь это имеет больше смысла. Так где же в команде perl определение соответствия регулярному выражению / захвата группы? Я вижу, вы написали '([0-9]*)ms$'- это предоставляется в качестве аргумента (а в строке другой аргумент)? И вывод из команды perl -eвставляется в printfкоманду bash , чтобы заменить %s, верно? Спасибо, я надеюсь использовать это.
Демис
1
Вы передаете регулярное выражение, заключенное в одинарные кавычки, в качестве единственного аргумента для функции regex bash. Пример
wytten