SED: несколько шаблонов на одной строке, как сопоставить / разобрать первый - PullRequest
15 голосов
/ 13 марта 2012

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

My 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 не говорит, заменить все элементы до 'NUM:' на '' (пусто)?Почему это соответствует всегда последнему вхождению?

Спасибо!

Ответы [ 4 ]

21 голосов
/ 13 марта 2012

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

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, мы эффективно разбиваем строку на управляемые части.

11 голосов
/ 13 марта 2012

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

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

Вы можете использовать perl в качестве замены для sed с параметрами -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
3 голосов
/ 14 марта 2012

Если число определяется цифрами после 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 //'
0 голосов
/ 13 марта 2012

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

sed -r 's/^(.*NUM:)(.*NUM:.*)$/\2/'
...