Коли регулярний вираз містить групи, може бути більше одного способу зіставити рядок проти нього: регулярні вирази з групами неоднозначні. Наприклад, розглянемо регулярне вираження ^.*\([0-9][0-9]*\)$та рядок a12. Є дві можливості:
- Матч
aпроти .*і 2проти [0-9]*; 1узгоджується з [0-9].
- Матч
a1проти .*і порожній рядок проти [0-9]*; 2узгоджується з [0-9].
Sed, як і всі інші інструменти regexp там, застосовує найдавніше правило найдовшого збігу: спочатку намагається зіставити першу частину змінної довжини проти рядка, який є максимально довгим. Якщо він знайде спосіб зіставити решту рядка з рештою регулярного виразу, добре. В іншому випадку sed намагається наступну найдовшу відповідність для першої частини змінної довжини і повторює спробу.
Тут спочатку зустрічається найдовший рядок a1проти .*, тому група відповідає лише 2. Якщо ви хочете, щоб група почала працювати раніше, деякі двигуни регулярного генерування дозволяють зробити .*менш жадібними, але sed не має такої функції. Тож вам потрібно усунути неоднозначність за допомогою якогось додаткового якоря. Вкажіть, що ведучий .*не може закінчуватися цифрою, так що перша цифра групи є першим можливим збігом.
Якщо група цифр не може бути на початку рядка:
sed -n 's/^.*[^0-9]\([0-9][0-9]*\).*/\1/p'
Якщо група цифр може бути на початку рядка, а ваш sed підтримує \?оператора за додатковими частинами:
sed -n 's/^\(.*[^0-9]\)\?\([0-9][0-9]*\).*/\1/p'
Якщо група цифр може знаходитись на початку рядка, дотримуючись стандартних конструкцій регулярних виразів:
sed -n -e 's/^.*[^0-9]\([0-9][0-9]*\).*/\1/p' -e t -e 's/^\([0-9][0-9]*\).*/\1/p'
До речі, саме це найдавніше правило найдовшого збігу змушує [0-9]*відповідати цифрам після першого, а не наступного .*.
Зауважте, що якщо на рядку є кілька послідовностей цифр, ваша програма завжди буде витягувати останню послідовність цифр, знову ж таки через найдавніше правило найдовшого збігу, застосоване до початкового .*. Якщо ви хочете витягнути першу послідовність цифр, вам потрібно вказати, що те, що відбувається раніше, - це послідовність нецифрових цифр.
sed -n 's/^[^0-9]*\([0-9][0-9]*\).*$/\1/p'
Більш загально, щоб витягнути першу відповідність регулярного вираження, потрібно обчислити заперечення цього регулярного виразу. Хоча теоретично це завжди можливо, розмір заперечення зростає в експоненціальній залежності від розміру виразного відриву, який ви заперечуєте, тому це часто недоцільно.
Розглянемо ваш інший приклад:
sed -n 's/.*\(CONFIG_[a-zA-Z0-9_]*\).*/\1/p'
Цей приклад насправді виставляє ту саму проблему, але ви її не бачите на типових матеріалах. Якщо ви його годуєте hello CONFIG_FOO_CONFIG_BAR, тоді команда, що надходить вище, виводить CONFIG_BAR, а не CONFIG_FOO_CONFIG_BAR.
Є спосіб надрукувати перший матч з sed, але це трохи хитро:
sed -n -e 's/\(CONFIG_[a-zA-Z0-9_]*\).*/\n\1/' -e T -e 's/^.*\n//' -e p
(Передбачається , що ваш SED підтримує \nозначає новий рядок в sтексті заміни.) Це працює тому , що СЕД виглядає для самого раннього матчу регулярного виразу, і ми не будемо намагатися відповідати тому , що передує CONFIG_…біт. Оскільки у рядку немає нової лінії, ми можемо використовувати її як тимчасовий маркер. TКоманда говорить здавайтеся , якщо попередня sкоманда не збігається.
Коли ви не можете зрозуміти, як щось робити в sed, перетворіться на awk. Наступна команда друкує найдавнішу найдовшу відповідність регулярного виразу:
awk 'match($0, /[0-9]+/) {print substr($0, RSTART, RLENGTH)}'
І якщо вам здається, що це просто, скористайтеся Perl.
perl -l -ne '/[0-9]+/ && print $&' # first match
perl -l -ne '/^.*([0-9]+)/ && print $1' # last match