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 '[^|]+'

Смотрите другую онлайн-демонстрацию.

Другие вопросы по тегам