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'
Что это делает:
- Положить
\n
в конце строки, чтобы действовать в качестве маркера. - Попробуйте найти число перед маркером и поставить его в конце строки (после маркера).
- Если номер был найден, перейдите к 2 выше.
- Если перед маркером не осталось ни одного номера, удалите все до номеров.
- Если число находится в строке, напечатайте его (для обработки случая, когда номер не найден.
Это также может быть сделано наоборот, сначала отбрасывая строки без цифр:
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/'