Як надрукувати відповідний шаблон регулярного вираження за допомогою awk?


109

Використовуючи awk, мені потрібно знайти слово у файлі, що відповідає шаблону регулярних виразів.

Я хочу лише роздрукувати слово, узгоджене з малюнком.

Отже, якщо в рядку, у мене є:

xxx yyy zzz

І візерунок:

/yyy/

Я хочу лише отримати:

yyy

EDIT: завдяки kurumi мені вдалося написати щось подібне:

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

49
Так printяк це дія за замовчуванням: awk '/pattern/' fileбуде достатньо.
Johnsyweb

18
@Johnsyweb, так, я знаю цей факт. Для початківця, як marverix, це означало бути більш наочним.
kurumi

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

Ось приклад використання програми GNU awk():

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: Щоб відповісти на цю останню частину, всі потрібні конструкції - у відповіді kurumi і моїй власній.
Johnsyweb

Чудова відповідь. Просто я хотів би, щоб тут було пояснення, бо я лінивий. Але саме тому я використовую AWK!
lukas.pukenis

Що робити, якщо я хочу зробити щось із результатом відповідності, крім того, щоб надрукувати його? Наприклад, я хочу додати всі збіги до масиву.
Evya2005

@ evya2005: Ви можете просто замінити виклик Ron print на призначення, яке вам потрібно.
Johnsyweb

це не працює для мене. тільки друкарські роботи. ти можеш показати мені приклад?
Evya2005

36

gawk може отримати відповідну частину кожного рядка, використовуючи це як дію:

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

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


13

Якщо вас цікавить лише останній рядок введення, і ви очікуєте знайти лише один збіг (наприклад, частину підсумкового рядка команди оболонки), ви також можете спробувати цей дуже компактний код, прийнятий з " Як друкувати збіги regexp". використовуючи `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

Чому ви додали "хвіст -n1"? Це мало би спрацювати без нього, ні?
Артур 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


Для не-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)
Juan

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.