Regex -> извлечение вхождений фиксированной позиции из сложной строки
У меня есть строка, подобная этой ниже (извлечение nvram), которая используется Tinc VPN для определения сетевых хостов:
1<host1<host1.network.org<<0<10.10.10.0/24<<Ed25519PublicKey = 8dtRRgAaTbUNtPxW9U3nGn6U7uvfIPwRo1wnx7xMIUH<Subnet = 10.10.3.0/24>1<host2<host2.network.org<<0<10.10.9.0/24<<Ed25519PublicKey = irn48tqF2Em4rIG0ggBmpEfaVKtkl6DmGdSzTHMmVEI<>0<host3<host3.network.org<<0<10.10.11.0/24<<Ed25519PublicKey = wQt1sFwOsd1hnBaNGHq4JDyib22fOg1YqzOp0p08ZTD<>
Я пытаюсь извлечь из вышесказанного:
host1.network.org
host2.network.org
host3.network.org
Имя хоста и ключи составлены, но структура входной строки точна. Кстати, конечный узел также может быть определен как IP-адреса, поэтому я пытаюсь извлечь то, что находится между вторым вхождением "<" и первым вхождением "<<". Поскольку это многократное совпадение, вхождения учитываются либо после начала строки, либо после символа ">". Таким образом, вышеизложенное можно прочитать следующим образом:
1<host1<host1.network.org<<0<10.10.10.0/24<<Ed25519PublicKey = 8dtRRgAaTbUNtPxW9U3nGn6U7uvfIPwRo1wnx7xMIUH<Subnet = 10.10.3.0/24>
1<host2<host2.network.org<<0<10.10.9.0/24<<Ed25519PublicKey = irn48tqF2Em4rIG0ggBmpEfaVKtkl6DmGdSzTHMmVEI<>
0<host3<host3.network.org<<0<10.10.11.0/24<<Ed25519PublicKey = wQt1sFwOsd1hnBaNGHq4JDyib22fOg1YqzOp0p08ZTD<>
Поскольку мне нужна эта информация в сценарии оболочки, я предполагаю, что мне нужно хранить каждый хост /IP как элемент массива.
Я использовал regexp онлайн-редакторы, и мне удалось обработать эту строку:
^[0|1]<.*?(\<(.*?)\<<)|>[0|1]<.*?(\<(.*?)\<)
Однако я бегу
grep -Eo '^[0|1]<.*?(\<(.*?)\<<)|>[0|1]<.*?(\<(.*?)\<)'
против исходного жала я получаю полную строку взамен, поэтому я должен делать что-то не так: - /
PS работает на buysbox: `BusyBox v1.25.1 (2017-05-21 14:11:58 CEST) двоичный файл с несколькими вызовами.
Использование: grep [-HhnlLoqvsriwFE] [-m N] [-A/B/C N] ШАБЛОН /-e ШАБЛОН.../-f ФАЙЛ [ФАЙЛ]...
Поиск ШАБЛОНА в ФАЙЛАХ (или STDIN)
-H Add 'filename:' prefix
-h Do not add 'filename:' prefix
-n Add 'line_no:' prefix
-l Show only names of files that match
-L Show only names of files that don't match
-c Show only count of matching lines
-o Show only the matching part of line
-q Quiet. Return 0 if PATTERN is found, 1 otherwise
-v Select non-matching lines
-s Suppress open and read errors
-r Recurse
-i Ignore case
-w Match whole words only
-x Match whole lines only
-F PATTERN is a literal (not regexp)
-E PATTERN is an extended regexp
-m N Match up to N times per file
-A N Print N lines of trailing context
-B N Print N lines of leading context
-C N Same as '-A N -B N'
-e PTRN Pattern to match
-f FILE Read pattern from file`
Спасибо!
2 ответа
ОК, нет ответа на мой комментарий, поэтому я введу его как ответ. Как насчет
\w*[a-z]\w*(\.\w*[a-z]\w*)+
Он соответствует как минимум двум частям полностью определенного имени, разделенных точкой.
grep -Eo '\w*[a-z]\w*(\.\w*[a-z]\w*)+'
доходность
host1.network.org
host2.network.org
host3.network.org
(при условии, что ваша строка введена в stdin;)
Ваше регулярное выражение основано на захвате групп и grep
Вы можете получить только полные совпадения. Кроме того, вы используете -E
(POSIX ERE), в то время как ваше регулярное выражение на самом деле не совместимо с POSIX ERE, так как содержит ленивые кванторы, которые не поддерживаются этим ароматом.
Я думаю, что вы можете извлечь все<
символы между <
а также <<
затем с цифрой, а затем <
с регулярным выражением PCRE (-P
опция):
s='1<host1<host1.network.org<<0<10.10.10.0/24<<Ed25519PublicKey = 8dtRRgAaTbUNtPxW9U3nGn6U7uvfIPwRo1wnx7xMIUH<Subnet = 10.10.3.0/24>1<host2<host2.network.org<<0<10.10.9.0/24<<Ed25519PublicKey = irn48tqF2Em4rIG0ggBmpEfaVKtkl6DmGdSzTHMmVEI<>0<host3<host3.network.org<<0<10.10.11.0/24<<Ed25519PublicKey = wQt1sFwOsd1hnBaNGHq4JDyib22fOg1YqzOp0p08ZTD<>'
echo $s | grep -oP '(?<=<)[^<]+(?=<<[0-9]<)'
Смотрите демо-версию регулярного выражения и grep
демо
Выход:
host1.network.org
host2.network.org
host3.network.org
Вот, (?<=<)
является положительным взглядом, который проверяет только <
присутствие сразу слева от текущего местоположения, но не добавляет <
к значению совпадения, [^<]+
соответствует 1+ символов кроме <
а также (?=<<[0-9]<)
(положительный взгляд) требует <<
затем цифра, а затем <
но опять же не добавляет эти символы в матч.
Если у вас нет опции PCRE в grep
попробуйте заменить весь текст, который вам не нужен, символом char, а затем либо разделить на awk, либо используйте grep
:
echo $s | \
sed 's/[^<]*<[^<]*<\([^<][^<]*\)<<[0-9]<[^<]*<<[^<]*[<>]*/|\1/g' | \
grep -oE '[^|]+'
Смотрите другую онлайн-демонстрацию.