正则表达式 -> 从复杂字符串中提取固定位置出现
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 '[^|]+'
好的,我的评论没有回复所以我将输入它作为答案。怎么样
\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
(假设您的字符串已输入标准输入;)
我有一个像下面这样的字符串(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 '[^|]+'
好的,我的评论没有回复所以我将输入它作为答案。怎么样
\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
(假设您的字符串已输入标准输入;)