正则表达式 grep 外部 IP 也带回内部 IP - 为什么?

Regex grep external IP brings back internal IP as well - why?

我这里有这个 grep 操作,可以从 ifconfig:

的输出中为您提供 external IP
ipa=$(ifconfig | grep -Po "inet addr:\K[^\s]+" | grep -v "^127")

我希望只使用一个 grep,所以我尝试了以下方法,部分成功:

ipa=$(ifconfig | grep -Po "inet addr:\K[0-9]{1,3}?\.[0-9]{1,3}?\.[0-9]{1,3}?\.[0-9]{1,3}?")

部分成功,因为它还带了一个space加上内部IP,不知为何:

MY_IP_ADDRESS 127.0.0.1

为什么会这样?我的意思是,为什么 也添加了 space + 环回,在仍然使用单个 grep 的同时可以做些什么来防止这种情况发生?环回甚至不是 ifconfig 输出的相关行的一部分。

有几种方法可以实现这一点,使用 ifconfigipdig 或我个人最喜欢的 myip. Furthermore, there are even more ways to optimize your regex, many of which you have probably already seen in the comments of your

但是,按字面意思回答你,无需重写你的命令或强加个人喜好,你可以达到排除的预期结果通过简单地指定您 do 想要获取的接口作为 ifconfig 的第一个参数来获取环回地址。默认情况下(即没有参数),ifconfig 显示 所有 当前活动接口的状态。

像这样应该就足够了:

# Replace "eth0" with the appropriately configured static inet address' interface
# ... is your `grep` pipe
ifconfig "eth0" ...

man ifconfig

If no arguments are given, ifconfig displays the status of the currently active interfaces.

鉴于您已经在使用 grep -P,您可以简单地添加一个否定断言:

ipa=$(ifconfig | grep -Po 'inet addr:\K(?!127\.)\d{1,3}.\d{1,3}\.\d{1,3}\.\d{1,3}')

您的原始问题的正则表达式(因此进行了编辑)也将接受点之间的零数字;我也修复了这个问题并简化了结果,希望能稍微提高易读性。

\K 是一个 Perl 创新,它说 "if you match through to here, forget the text which got to this point" 这意味着 inet addr: 上的匹配不会包含在 grep -o 打印的 "matched text" 中.

表达式(?!127\.)是一个否定的先行断言。简而言之,它表示 "if this regex would match now, this is not a match"。换句话说,正则表达式引擎会短暂暂停,记下它在文本中的位置,然后 "peeks ahead" 并尝试匹配 127\.。如果成功,它会在此时放弃尝试匹配,并继续尝试匹配字符串中稍后点的整个表达式(因此,如果稍后要在中找到第二次出现 inet addr:同一条线,你仍然可以从那里得到匹配。

最后,我将引号改为单引号。这在这里并不重要,但我建议在所有正则表达式周围使用单引号 unless you specifically require the shell to perform variable replacements in the regex 或类似的东西。

至于解释你所看到的,输出中确实没有 space。 grep 输出两行,因为它找到了两个匹配项(当然,我们现在使用负前瞻来防止这种情况发生;但是如果您配置了多个接口,您仍然可以获得多个结果)。如果您看到 space,那是因为您在回显时没有使用双引号,如 echo "$ipa".

如评论中所述,如果得到 bash: !127: event not found,则需要 set +H 或将命令放在脚本中;或者,像我在上一段中推荐的那样使用单引号。除非你沉迷于遗留的 Csh-style history management features in Bash(说真的,现在是谁?),我建议你通过将命令 set +H 放入你的 .bash_profile 或类似的命令来永久更改。

可选:重构正则表达式

您可以重构您的正则表达式以使其更紧凑但可能稍微不太清晰:

ipa=$(ifconfig | grep -Po 'inet addr:\K(?!127\.)\d{1,3}(?:.\d{1,3}){3}')

一个更短的方法是这样的:

ipa=$(ifconfig | grep -Po 'inet addr:\K(?!127\.)[.\d]+')

注意相同的 \K(?!127\.) 模式,还有新的 [.\d]+ 取代了 \d{1,3}.\d{1,3}\.\d{1,3}\.\d{1,3}') 模式。这稍微不太精确,但对于这种情况可能已经足够好了。如果您的输入来自 ifconfig 并且您已经看到 inet addr: 路标,那么匹配尽可能多的数字和点应该总能为您找到您正在寻找的 IP 地址。

根据您的需要,您仍然可以在前瞻中添加更多要阻止的内容。为了防止它也匹配内部网络,比如

(?!127\.|10\.|172\.(?:1[6-9]|2[0-9]|3[01])|192\.168\.)

将阻止提取所有 IANA 保留的专用网络块中的地址,包括环回。

正如 Triplee 在评论中指出的那样(Triplee 的回答应该被赞成):

  1. 正则表达式也匹配环回,因为它也是一个具有 init addr: 前体的 IP。
  2. 添加 space 是因为我使用 echo $ipa 而不是 "echo "$ipa"

考虑到我也得到了回环,因为正则表达式也匹配它(我一开始没有注意到它,因为它接近 ifconfig 输出的末尾,我所做的是使用grep -m1参数,这个参数使得grep只带第一个匹配(而且外网IP确实更早,最先找到),所以结束命令为:

ipa=$(ifconfig | grep -Po -m1 "inet addr:\K[0-9]{1,3}?\.[0-9]{1,3}?\.[0-9]{1,3}?\.[0-9]{1,3}?")

然而,正如 Triplee 的评论所提到的那样,假设第一个匹配项是外部 IP 而不是环回从原则上讲是有问题的 --- ifconfig 明天可能会改变,让环回作为第一个,所以应该使用 Triplee 的这个单一 grep 解决方案,其中涉及 negative assertion:

ipa=$(ifconfig | grep -Po 'inet addr:\K(?!127\.)\d{1,3}.\d{1,3}\.\d{1,3}\.\d{1,3}')

或者 Triplee 的一个更短的替代方案,它还通过执行 set +H:

在交互式 shell 中包含 历史扩展预防
ipa=$(ifconfig | grep -Po 'inet addr:\K(?!127\.)[.\d]+')

注意:如果出错,需要执行set +H。尽管可以使用 set -H.

撤销此状态,但保持此状态没有问题

无论哪种方式,另一种最小方法是我在问题中发布的原始 2 grep 方法:

ipa=$(ifconfig | grep -Po "inet addr:\K[^\s]+" | grep -v "^127")

排除以127.开头的地址:

ifconfig | grep -Po '\binet addr:\K(?!127\.)\S+'

不包括 lo 适配器:

ifconfig | perl -nle'BEGIN { $/="" } next if /^lo\b/; print for /\binet addr:(\S+)/g'

只是一个特定的适配器:

ifconfig eth1 | grep -Po '\binet addr:\K\S+'

只是以太网适配器的第一个地址:

ifconfig | perl -nle'BEGIN { $/="" } if (/^eth.*?\binet addr:(\S+)/s) { print ; exit; }'