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 Це підтверджує, що ви не можете повторно використовувати відповідні групи.
Пітер Тіллеманс

3
Я віддаю перевагу 'perl -n -p -e ...' over awk майже для всіх випадків використання, оскільки він є більш гнучким, потужнішим і має синергетичніший синтаксис.
Пітер Тілманманс

15
gawk! = awk. Вони різні інструменти і gawkне доступні за замовчуванням у більшості місць.
Олі

6
ОП спеціально попросило невдале рішення, тому не думаю, що це відповідь.
Джоппе

6
@Joppe ви не можете дати розвiдне рішення, якщо не буде рішення. У рядку 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 '.*'

Захопіть першу групу захоплення регулярних виразів для кожного рядка у файлі

$ cat filename | regex '(.*)' 1

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 'match($0, /.*(http.*?)\$/) { print substr($0,RSTART,RLENGTH) }'
Ед Мортон


1
Ед Мортон: це заслуговує на відповідь найвищого рівня. редагувати: ум ... це друкує RewriteRule (.*) http://www.mysite.net/$для мене, що більше, ніж підгрупа.
чемпіон


4

Ви також можете імітувати захоплення ванільним awk, без розширень. Це не інтуїтивно зрозуміло:

крок 1. використовуйте gensub для оточення збігів з деяким символом, який не відображається у вашому рядку. крок 2. Використовуйте розділення проти символу. крок 3. Кожен інший елемент розбитого масиву - це ваша група захоплення.

$ echo 'ab cb ad' | awk '{split (gensub (/ a ./, SUBSEP "&" SUBSEP, "g", $ 0), шапка, SUBSEP); кришка друку [2] "|" шапка [4]; } '
ab | оголошення

3
Я майже впевнений, що gensubце gawkспецифічна функція. Що ви отримуєте від awk, якщо набираєте awk --version; -?). Успіхів усім.
обстріл

6
Я повністю впевнений, що gensub - це гаук-ізм, хоча BusyBox awk також має його. Ця відповідь також може бути реалізована за допомогою gsub, хоча:echo 'ab cb ad' | awk '{gsub(/a./,SUBSEP"&"SUBSEP);split($0,cap,SUBSEP);print cap[2]"|"cap[4]}'
сумнівний

3
gensub () - це розширення gawk. Інші варіанти awk також можуть його реалізувати, але це все ще не POSIX. Спробуйте gawk --posix '{gsub (...)}', і він поскаржиться
MestreLion

2
@MestreLion, ти маєш на увазі, що скаржиться gawk --posix '{gensub(...)}'.
сумнівним

1
Незважаючи на те, що ви помилялися з тим, що POSIX awk мав цю gensubфункцію, ваш приклад застосовано до дуже обмеженого сценарію: весь візерунок згрупований, він не може відповідати чомусь подібному до всіх, key=(value)коли я хочу витягнути лише valueчастини.
Мяу

2

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

функція regex {perl -n -e "/ $ 1 / && printf \"% s \ n \ "," '$ 1'}

Я виявив, що це спрацювало краще, ніж функція bash на базі opsb для наступного аргументу регулярного виразу, оскільки я не хочу, щоб "ms" друкувалася.

'([0-9]*)ms$'

Я вважаю за краще це рішення, оскільки ви можете бачити частини групи, які обмежують захоплення, а також опускаючи їх. Однак хтось може пояснити, як це працює? Я не можу змусити цей синтаксис perl працювати належним чином у BASH, тому що я його не дуже добре розумію - особливо подвійні / $1
одноцитати

Це не те, що я робив до цього чи раніше, але озираючись на те, що він робить, - це об'єднання двох рядків, перша рядок - у подвійних лапках (перший рядок містить вбудовані подвійні лапки, уникнуті зі зворотною косою рисою), а другий рядок - в одиничних лапках . Тоді результат цього конкатенації подається як аргумент perl -e. Також потрібно знати, що перший $ 1 (той, що знаходиться в подвійних лапках) заміщений першим аргументом функції, а другий $ 1 (той, що знаходиться в межах однієї лапки) залишається недоторканим. Дивіться цей приклад
wytten

Я бачу, зараз це має трохи більше сенсу. Отже, де в команді perl визначено збіг з регулярними виразками / груповим захопленням? Я бачу, ви писали '([0-9]*)ms$'- це подано як аргумент (а рядок - ще один аргумент)? І вихід із perl -eцього тексту вставляється в printfкоманду bash , щоб замінити %s, чи правильно це? Дякую, я сподіваюся використати це.
Деміс

1
Ви передаєте регулярний вираз, укладений в одиночні лапки, як єдиний аргумент функції bashe regex. Приклад
wytten
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.