Linux脚本提取IP地址

50

我希望在我的Debian 7.3上编写一个大型脚本(类似于翻译和更加用户友好的环境)。但是我遇到了一个问题,我只想使用命令给出的部分信息。例如,我的ifconfig看起来像:

eth0      Link encap:Ethernet  HWaddr 08:00:27:a3:e3:b0  
          inet addr:192.168.1.103  Bcast:192.168.1.255  Mask:255.255.255.0
          inet6 addr: fe80::a00:27ff:fea3:e3b0/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:1904 errors:0 dropped:0 overruns:0 frame:0
          TX packets:2002 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:1309425 (1.2 MiB)  T

我想在这一行中仅显示IP地址: echo "Your IP address is: (IP_ADDRESS)"。是否有任何命令可以让我做到这一点,以便在流中搜索我想要获取的信息?我知道grep和sed,但我不是很擅长使用。

编辑:首先感谢您帮助我解决这个问题,现在我知道了更多。其次,要说项目正在进行中。如果有人对此感兴趣,请私信我。


1
还要查看bash手册中的管道文档。 - Alex Brown
3
你所尝试做的事情存在一个根本性问题:机器通常会有多个IP地址(有一个用于有线连接,一个用于无线连接,一个用于虚拟机连接等)。你需要考虑要选择哪一个。 - James Kingsbery
@JamesKingsbery 我没有考虑到这一点。我稍后会考虑一下。谢谢你给我展示下一个问题。PS:谢谢你给我发送grep命令的参考手册。它真的帮了我很多。 - user3232381
15个回答

106
如果目标是找到连接到互联网的IP地址,则这应该是一个不错的解决方案。
编辑2021:添加了“sed”和“grep”版本以及新的“awk”版本(其中一些是gnu)。
要查找连接到互联网的使用的IP地址,我们可以使用ip route命令。在较新版本的Linux中,您将获得更多信息,典型输出如下:
ip route get 8.8.8.8
8.8.8.8 via 10.36.15.1 dev ens160 src 10.36.15.150 uid 1002
    cache

要获取IP,您需要在src之后使用awksedgrep查找IP。

ip route get 8.8.8.8 | awk -F"src " 'NR==1{split($2,a," ");print a[1]}'
ip route get 8.8.8.8 | awk 'match($0,/src (\S*)/,a)&&$0=a[1]'
ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++)if($i~/src/)$0=$(i+1)}NR==1'

ip route get 8.8.8.8 | sed -E 's/.*src (\S+) .*/\1/;t;d'
ip route get 8.8.8.8 | sed 's/.*src \([^ ]*\).*/\1/;t;d'
ip route get 8.8.8.8 | sed  -nE '1{s/.*?src (\S+) .*/\1/;p}'

ip route get 8.8.8.8 | grep -oP 'src \K[^ ]+'
10.36.15.150

如果你喜欢使用 awksed 或者 grep,可以通过这些工具来查找接口名称

ip route get 8.8.8.8 | awk -F"dev " 'NR==1{split($2,a," ");print a[1]}'
ip route get 8.8.8.8 | awk 'match($0,/dev (\S*)/,a)&&$0=a[1]'
ip route get 8.8.8.8 | awk '{for(i=1;i<=NF;i++)if($i~/dev/)$0=$(i+1)}NR==1'

ip route get 8.8.8.8 | sed -E 's/.*?dev (\S+) .*/\1/;t;d'
ip route get 8.8.8.8 | sed 's/.*dev \([^ ]*\).*/\1/;t;d'
ip route get 8.8.8.8 | sed  -nE '1{s/.*?dev (\S+) .*/\1/;p}'

ip route get 8.8.8.8 | grep -oP 'dev \K[^ ]+'
ens192

ip route 不会打开任何连接,它只显示到达 8.8.8.8 所需的路由。 8.8.8.8 是 Google 的 DNS。

如果您想将此存储到变量中,请执行以下操作:

my_ip=$(ip route get 8.8.8.8 | awk -F"src " 'NR==1{split($2,a," ");print a[1]}')

my_interface=$(ip route get 8.8.8.8 | awk -F"dev " 'NR==1{split($2,a," ");print a[1]}')

为什么其他解决方案可能会失败:

ifconfig eth0

  • 如果您的接口有另一个名称(eno1、wifi、venet0等)
  • 如果您有多个接口
  • IP连接方向不是多个IF列表中的第一个

Hostname -I

  • 可能只会得到127.0.1.1
  • 在所有系统上都无法使用。
  • 给出所有IP,而不仅仅是连接到互联网的那个。-I,--all-ip-addresses适用于主机的所有地址

3
这是最佳解决方案,因为它不依赖于预先知道接口名称。如果接口名称更改,脚本也不会出错。另外,请注意,如果您有不同的接口用于不同的子网/WAN等,您可以通过替换8.8.8.8(只需使用您所知道的网络上的IP)来选择正确的IP。 - Jay
非常优雅的解决方案。可惜它在OS X上不可用。即使安装了iproute2mac软件包,从命令输出中解析出的“src”数据也不存在。如果有人知道,在OS X网络命令中可能替换“ip route get”的内容,我会很好奇。 - Mark Edington
@NickBastin 你错了。这个解决方案是找到用于连接互联网的接口。如果您没有互联网,那么您就不需要这个。您不需要访问此IP地址,它会找到正确的接口。 - Jotne
@NickBastin 没错,但从他发布的数据以及他接受了一个与我相同的回答来看,你可以假设他正在寻找外部接口的IP。其他人也做出了同样的假设,因为我的答案有65个赞。我的帖子也是一条评论,不要使用:ifconfig eth0。你可以自行理解它的含义 :) - Jotne
1
较新的iproute2有一个输出JSON的选项,这对于解析来说更加可靠和简单。以下是一个示例:ip --json route get 8.8.8.8 | jq -r '.[].prefsrc'。不过需要安装额外的程序。 - Chih-Hsuan Yen
显示剩余11条评论

43

获取你的IP地址:

echo `ifconfig eth0 2>/dev/null|awk '/inet addr:/ {print $2}'|sed 's/addr://'`

这将为您提供eth0的IP地址。

编辑:由于Ubuntu的最新版本中接口名称的更改,此方法不再适用。您可以尝试使用以下命令来获取所有IP地址:hostname --all-ip-addresseshostname -I,两者都可以达到相同的效果(显示主机的所有IP地址)。


6
“echo”没有起到任何作用。而且,你永远不需要在awk中使用“sed”,因为awk可以完成sed能做的所有事情。 - Ed Morton
7
不应使用反引号````,应该使用圆括号$(code)。如果IF不是eth0,则也会失败。可以将其改写为:ifconfig eth0 | awk -F"[: ]+" '/inet addr:/ {print $4}' - Jotne
6
考虑使用 hostname -I。根据 @DanilaV. 的评论,现在网络接口的命名可能不是以 ethX 开头,许多Linux发行版已经开始使用 em 作为接口的前缀。 - icdevppl
1
在Ubuntu >= 15.x中,他们将接口从eth0重命名为eno1,因此这个脚本根本无法工作。 - Jotne
1
感谢您的评论。我将“hostname --all-ip-addresses”添加到解决方案中,这与hostname -I相同(感谢icdevppl!)。 - Marco Hegenberg
显示剩余2条评论

39

这里提到的命令确实很好,但我的项目仍在进行中,我不确定我将需要什么。 - user3232381
1
不总是会得到一个单一的IP。在我的VPS服务器上,我得到了这个输出 127.0.0.2 143.127.52.130 2a00:dee0:ed3:83:245:70:fc12:d196,因此我需要添加一些过滤器。 - Jotne
2
@Jotne 如果你有多个IP地址,你可以像这样截取输出到你想要的一个(例如第一个):hostname -I | cut -f1 -d' ',虽然我同意如果接口的顺序可能会改变,使用 ip 工具更好。 - user000001
2
喜欢它 - 假设只有一个地址,这是完美的!这里还有另一种通过“ip”获取它的粗略方法 ip addr | grep "scope global" | sed 's:^[^0-9]::' | sed 's:/.$::' - Pancho
1
主机名在OS X上没有-I选项,所以这只适用于Linux。 - Robert P. Goldman
这不是OS X的问题。问题在于你正在阅读一个5年前的答案。 - harogaston

16
ip -4 addr show eth0 | grep -oP "(?<=inet ).*(?=/)"

1
你能否添加更多的命令解释,以便我们能够理解其中的部分内容? - JB King
1
感谢您。改进的正则表达式为:"(?<=inet )[\d.]+(?=/)" - gue
如果您的接口未命名为“eth0”,则此操作将失败,而现代许多接口都不是这样命名的。 - Ian D. Allen

9

也许不是所有情况都适用(特别是当你有多个网卡时),以下方法可以帮助解决问题:

hostname -I | awk '{ print $1 }'

2
hostname -I。正如man所说:“不要对输出的顺序做任何假设。” - pvincent

6
ip route get 8.8.8.8| grep src| sed 's/.*src \(.* \)/\1/g'|cut -f1 -d ' '

2
感谢您回答这个问题!在 Stack Overflow 上,我们不鼓励仅包含代码的答案,因为原始提问者(或未来的读者)可能难以理解其背后的逻辑。请编辑您的答案并包含对代码的解释,以便其他人可以从中受益。谢谢! - Maximillian Laumeister

4

选择一项:

$ cat file
eth0      Link encap:Ethernet  HWaddr 08:00:27:a3:e3:b0
          inet addr:192.168.1.103  Bcast:192.168.1.255  Mask:255.255.255.0
          inet6 addr: fe80::a00:27ff:fea3:e3b0/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:1904 errors:0 dropped:0 overruns:0 frame:0
          TX packets:2002 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:1309425 (1.2 MiB)  T

$ awk 'sub(/inet addr:/,""){print $1}' file
192.168.1.103

$ awk -F'[ :]+' '/inet addr/{print $4}' file
192.168.1.103

4
  /sbin/ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'

这将取决于操作系统使用的语言(在本例中为英语)。 - Denio Mariz

4
在我看来,实现您所需的最简单和最优雅的方法是这样的:
ip route get 8.8.8.8 | tr -s ' ' | cut -d' ' -f7

ip route get [host] - 这条命令可以告诉你到达远程主机所使用的网关,例如:

8.8.8.8 via 192.168.0.1 dev enp0s3  src 192.168.0.109

tr -s ' '——移除所有额外的空格,现在文本格式更加统一,例如:

8.8.8.8 via 192.168.0.1 dev enp0s3 src 192.168.0.109

cut -d' ' -f7 - 将字符串截断成以空格分隔的字段,然后从中选择第7个字段,例如:

192.168.0.109

3

注:因为我刚刚花了一些时间来排除服务器上升级失败的问题,所以我想提醒大家一下。 结果发现,几年前我实施了一个测试,用于检查动态添加的接口(例如eth0:1)是否存在,如果存在,我会将某些程序绑定到eth0上的“主”IP地址。 基本上它是“ifconfig | grep ... | sed ...”解决方案的一个变化(加上检查“eth0:”的存在)。
此次升级引入了新的net-tools,并且输出略有改变:

旧的ifconfig:

eth0      Link encap:Ethernet  HWaddr 42:01:0A:F0:B0:1D
          inet addr:10.240.176.29  Bcast:10.240.176.29  Mask:255.255.255.255
          UP BROADCAST RUNNING MULTICAST  MTU:1460  Metric:1
          ...<SNIP>

新版本将显示如下内容:
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1460
      inet 10.240.212.165  netmask 255.255.255.255  broadcast 10.240.212.165
      ...<SNIP>

渲染对“eth0:”和“inet addr:”的搜索失败(不要在意名为“em0”,“br0”或“wlan0”的接口...)。当然,你可以检查“inet”(或“inet6”),并使addr:部分可选,但更仔细地看,你会发现几乎一切都改变了,“Mask”现在是“netmask”,...。 “ip路由…”建议很棒-也许:
_MyIP="$( ip route get 8.8.8.8 | awk 'NR==1 {print $NF}' )"
if [ "A$_MyIP" == "A" ]
then
    _MyIPs="$( hostname -I )"
    for _MyIP in "$_MyIPs"
    do
        echo "Found IP: \"$_MyIP\""
    done
else
    echo "Found IP: $_MyIP"
fi

嗯,无论如何都要解决这个问题。由于所有提出的解决方案都存在失败的情况,请检查可能的边缘情况-没有eth,多个eth和lo,'hostname -i' 何时会失败,然后决定最佳解决方案,检查其是否有效,否则选择第二好的。

干杯!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接