SED: несколько шаблонов на одной строке, как сопоставить / проанализировать первый

У меня есть файл, который содержит данные о номере телефона, а также некоторые бесполезные вещи. Я пытаюсь разобрать номера, и когда есть только 1 номер телефона / линия, это не проблема. Но когда у меня несколько чисел, sed совпадает с последним (хотя везде говорится, что оно должно совпадать только с первым шаблоном?), И я не могу получить другие числа...

Мои data.txt:

bla bla bla NUM:09011111111 bla bla bla bla NUM:08022222222 bla bla bla

Когда я анализировал данные, моей идеей было сначала удалить все "начальные" "бла бла бла" перед первым номером телефона (поэтому я ищу первое вхождение "NUM:"), затем я удаляю все вещи после номера телефона, и получить номер. После этого я хочу проанализировать следующее вхождение из оставшейся строки.

Так что теперь, когда я пытаюсь успокоить это, я всегда получаю последний номер в строке:

>sed 's/.*NUM://' data.txt
08022222222 bla bla bla
> 

Прежде всего я хотел бы понять, что не так с моим пониманием SED. Конечно, более эффективные предложения приветствуются! Разве моя команда sed не говорит, заменить все вещи перед 'NUM:' на '' (пусто)? Почему это соответствует всегда последнему вхождению?

Спасибо!

5 ответов

Решение

Это может работать для вас:

echo "bla bla bla NUM:09011111111 bla bla bla bla NUM:08022222222 bla bla bla" |
sed 's/NUM:/\n&/g;s/[^\n]*\n\(NUM:[0-9]*\)[^\n]*/\1 /g;s/.$//'
NUM:09011111111 NUM:08022222222

Проблема в том, что вы понимаете, что .* жадный, т. е. соответствует самому длинному совпадению, а не первому. Размещая уникального персонажа (\n sed использует его как разделитель строк, поэтому он не может существовать в строке) перед интересующей нас строкой (NUM:...) и удалив все, что не является уникальным персонажем [^\n]* сопровождаемый уникальным персонажем \nмы эффективно разбиваем строку на управляемые части.

Как вы уже знаете, sed регулярные выражения являются жадными и, насколько я могу судить, не могут быть сделаны не жадными.

Две альтернативы, которые не были рассмотрены до сих пор, - это просто использовать другие инструменты для такого типа сопоставления / извлечения.

Ты можешь использовать perl в качестве замены для сед с -pe параметры. Поддерживает ? не жадный модификатор:

$ perl -pe 's/.*?NUM://' data.txt
09011111111 bla bla bla bla NUM:08022222222 bla bla bla

Вы можете использовать -o опция GNU grep, чтобы получить только те биты ваших данных, которые соответствуют регулярному выражению:

$ egrep -o 'NUM:[0-9]*' data.txt 
NUM:09011111111
NUM:08022222222

Если число определяется цифрами после NUM::

sed -n -e 's/$/\n/' -e ':begin' \
  -e 's/\(NUM:[0-9][0-9]*\)\(.*\)\n\(.*\)/\2\n\3 \1/' \
  -e 'tbegin' -e 's/.*\n //' -e '/NUM/p'

Что это делает:

  1. Положить \n в конце строки, чтобы действовать в качестве маркера.
  2. Попробуйте найти число перед маркером и поставить его в конце строки (после маркера).
  3. Если номер был найден, перейдите к 2 выше.
  4. Если перед маркером не осталось ни одного номера, удалите все до номеров.
  5. Если число находится в строке, напечатайте его (для обработки случая, когда номер не найден.

Это также может быть сделано наоборот, сначала отбрасывая строки без цифр:

sed  -e '/NUM/!d' -e 's/$/\n/' -e ':begin' \
  -e 's/\(NUM:[0-9][0-9]*\)\(.*\)\n\(.*\)/\2\n\3 \1/' \
  -e 'tbegin' -e 's/.*\n //'
      sed -E 's/(-y)|(-f)|(\+incdir\+)/\n&/g' abcfile > cdeop

где abcfile будет иметь в качестве примера шаблоны -y / -f / +incdir+, при совпадении шаблонов перед ним будет вставлена ​​новая строка.

Вы можете использовать этот шаблон:

sed -r 's/^(.*NUM:)(.*NUM:.*)$/\2/'
Другие вопросы по тегам