正则表达式 -> 从复杂字符串中提取固定位置出现

Regex -> extracting fixed position occurrences from complex string

我有一个像下面这样的字符串(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<>

因为我在 shell 脚本中需要此信息,所以我想我需要将每个 host/IP 存储为数组的元素。

我使用了正则表达式在线编辑器,并设法计算出了这个字符串:

^[0|1]<.*?(\<(.*?)\<<)|>[0|1]<.*?(\<(.*?)\<)

但是我运行是

grep -Eo '^[0|1]<.*?(\<(.*?)\<<)|>[0|1]<.*?(\<(.*?)\<)'

最初的刺痛让我在 return 中得到了完整的字符串,所以我一定是做错了什么:-/

P.S。 运行宁在 buysbox 上: `BusyBox v1.25.1 (2017-05-21 14:11:58 CEST) 多调用二进制文件。

用法:grep [-HhnlLoqvsriwFE] [-m N] [-A/B/C N] PATTERN/-e PATTERN.../-f FILE [FILE]...

在文件(或标准输入)中搜索模式

    -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`

谢谢!

您拥有的正则表达式基于捕获组,而使用 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]<)'

参见regex demo and a grep demo

输出:

host1.network.org
host2.network.org
host3.network.org

这里,(?<=<) 是一个积极的后视,它只检查紧邻当前位置左侧的 < 存在,但不会将 < 添加到匹配值,[^<]+ 匹配除 <(?=<<[0-9]<) 以外的 1+ 个字符(正向前瞻)需要 <<,然后是数字,然后是 < 但同样不会添加这些字符匹配。

如果 grep 中没有 PCRE 选项,请尝试用一些字符替换所有不需要的文本,然后用 awk 拆分,或使用 grep:

echo $s | \ 
   sed 's/[^<]*<[^<]*<\([^<][^<]*\)<<[0-9]<[^<]*<<[^<]*[<>]*/|/g' | \ 
    grep -oE '[^|]+'

参见 another online demo

好的,我的评论没有回复所以我将输入它作为答案。怎么样

\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

(假设您的字符串已输入标准输入;)