如何在 Bash 脚本中将主机名解析为 IP 地址?

Eug*_*ash 514 linux networking bash dns

在 Bash 脚本中将主机名解析为 IP 地址的最简洁方法是什么?我正在使用Arch Linux

Chr*_*own 685

您可以使用getent, 随附glibc(因此您几乎可以肯定在 Linux 上拥有它)。这使用 gethostbyaddr/gethostbyname2 解决,因此还将检查/etc/hosts/NIS/etc:

getent hosts unix.stackexchange.com | awk '{ print $1 }'
Run Code Online (Sandbox Code Playgroud)

或者,正如下面Heinzi说,你可以使用dig+short参数(查询DNS服务器直接,不看/etc/hosts/ NSS /等):

dig +short unix.stackexchange.com
Run Code Online (Sandbox Code Playgroud)

如果dig +short不可用,以下任何一项都应该有效。所有这些都直接查询 DNS 并忽略其他解析方式:

host unix.stackexchange.com | awk '/has address/ { print $4 }'
nslookup unix.stackexchange.com | awk '/^Address: / { print $2 }'
dig unix.stackexchange.com | awk '/^;; ANSWER SECTION:$/ { getline ; print $5 }'
Run Code Online (Sandbox Code Playgroud)

如果您只想打印一个 IP,则将exit命令添加到awk的工作流程。

dig +short unix.stackexchange.com | awk '{ print ; exit }'
getent hosts unix.stackexchange.com | awk '{ print $1 ; exit }'
host unix.stackexchange.com | awk '/has address/ { print $4 ; exit }'
nslookup unix.stackexchange.com | awk '/^Address: / { print $2 ; exit }'
dig unix.stackexchange.com | awk '/^;; ANSWER SECTION:$/ { getline ; print $5 ; exit }'
Run Code Online (Sandbox Code Playgroud)

  • 使用`getent hosts <host>` 是不正确的,例如它可能会给出一个 IPv6 地址,而 IPv6 不起作用。正确的解决方案是在需要时使用`getent ahosts <host>` 来尝试 IPv6 和 IPv4。 (16认同)
  • DIG 不起作用,如果是 CNAME 它不会返回 IP。 (8认同)
  • 值得一提的是:host、dig 和 nslookup 似乎直接与 resolv.conf 中列出的服务器通信,而“getent hosts”如果启用则尊重本地主机文件和库级缓存(例如 nscd)。 (8认同)
  • 有时,`host` 可能会超时并且不返回任何内容。对于某些域,`dig +short` 可能会在第一行返回域别名。因此,要确保输出是 IPv4 地址,请使用 `dig +short example.com | grep -Eo '[0-9\.]{7,15}' | 头-1`。 (4认同)
  • 默认情况下,使用 dig 仅适用于 ipv4,其中主机提供 ipv4 和 ipv6 答案。这可能出乎意料。你可以试试`host www.google.com`、`dig +short www.google.com`、`host ipv6.google.com`、`dig +short ipv6.google.com`、`host www.facebook.com `,`dig +short www.facebook.com`。 (2认同)
  • `host` 将错误输出打印到 `stdout`,对于未解析的主机,`dig` 似乎也返回 `0`,`getent` 似乎不允许超时......我很惊讶这如此复杂。 (2认同)
  • `挖掘 +short unix.stackexchange.com | awk '/^[0-9]+\./ { 打印 ; exit }'` 考虑 CNAME。 (2认同)

man*_*ork 161

随着hostdnsutils包:

$ host unix.stackexchange.com
unix.stackexchange.com has address 64.34.119.12
Run Code Online (Sandbox Code Playgroud)

(根据评论更正了包名。请注意,其他发行版host在不同的包中有:Debian/Ubuntu bind9-host、openSUSE bind-utils、Frugalware bind。)

  • 这个答案值得严重反对。`host` 是一个 DNS 工具(类似于 `nslookup`),所以它只在 DNS 中查找主机,而不是在例如 `/etc/hosts` 中。所以它 ** 不是 ** OP 问题的答案。 (4认同)
  • 请注意`host` 有时会返回多行输出(在重定向的情况下),您需要`host unix.stackexchange.com | tail -n1` 如果您只想要带有 IP 地址的行。 (2认同)

gre*_*ire 59

我的机器上有一个工具似乎可以完成这项工作。手册页显示它似乎与 mysql 一起......这是你如何使用它:

resolveip -s unix.stackexchange.com
64.34.119.12
Run Code Online (Sandbox Code Playgroud)

如果无法解析主机名,此工具的返回值与 0 不同:

resolveip -s unix.stackexchange.coma
resolveip: Unable to find hostid for 'unix.stackexchange.coma': host not found
exit 2
Run Code Online (Sandbox Code Playgroud)

更新 在 Fedora 上,它带有 mysql-server :

yum provides "*/resolveip"
mysql-server-5.5.10-2.fc15.x86_64 : The MySQL server and related files
Dépôt         : fedora
Correspondance depuis :
Nom de fichier      : /usr/bin/resolveip
Run Code Online (Sandbox Code Playgroud)

我想它会为您的脚本创建一个奇怪的依赖项...

  • `getent`,如另一个答案中详述的,也查看 /etc/hosts,并带有 glibc,因此不依赖于 Linux 系统。 (9认同)
  • 这似乎是这里唯一使用操作系统内置解析器的解决方案 - 因此适用于 /etc/hosts 和 DNS。 (7认同)

Hei*_*nzi 51

以下命令 usingdig允许您直接读取结果,无需任何 sed/awk/etc。魔法:

$ dig +short unix.stackexchange.com
64.34.119.12
Run Code Online (Sandbox Code Playgroud)

dig也包含在dnsutils包中。


注意:即使名称无法解析,dig也有返回值0。因此,您需要检查输出是否为空而不是检查返回值:

hostname=unix.stackexchange.com

ip=`dig +short $hostname`

if [ -n "$ip" ]; then
    echo IP: $ip
else
    echo Could not resolve hostname.
fi
Run Code Online (Sandbox Code Playgroud)

注 2:如果一个主机名有多个 IP 地址(debian.org例如try ),则将返回所有这些地址。到目前为止,这个“问题”影响了这个问题中提到的所有工具:

  • 请注意,如果域具有 CNAME 条目,则其域可能会打印在第一行而不是 IP 地址。 (3认同)

小智 48

getent hosts unix.stackexchange.com | cut -d' ' -f1
Run Code Online (Sandbox Code Playgroud)

  • 还可以考虑 `ahosts`、`ahostsv4`、`ahostsv6` 和 `getent`。 (4认同)

jfg*_*956 32

迄今为止给出的解决方案大多适用于更简单的情况:主机名直接解析为单个 IPv4 地址。这可能是您需要解析主机名的唯一情况,但如果不是,下面将讨论您可能需要处理的某些情况。

Chris Down 和 Heinzi 简要讨论了主机名解析为多个 IP 地址的情况。在这种情况下(以及以下其他情况),假设主机名直接解析为单个 IP 地址的基本脚本可能会中断。下面是一个主机名解析为多个 IP 地址的示例:

$ host www.l.google.com
www.l.google.com has address 209.85.148.147
www.l.google.com has address 209.85.148.103
www.l.google.com has address 209.85.148.99
www.l.google.com has address 209.85.148.106
www.l.google.com has address 209.85.148.105
www.l.google.com has address 209.85.148.104
Run Code Online (Sandbox Code Playgroud)

但什么是www.l.google.com?这是需要引入别名情况的地方。让我们看看下面的例子:

$ host www.google.com
www.google.com is an alias for www.l.google.com.
www.l.google.com has address 74.125.39.103
www.l.google.com has address 74.125.39.147
www.l.google.com has address 74.125.39.105
www.l.google.com has address 74.125.39.99
www.l.google.com has address 74.125.39.106
www.l.google.com has address 74.125.39.104
Run Code Online (Sandbox Code Playgroud)

Sowww.google.com不直接解析为 IP 地址,而是解析为一个别名,该别名本身解析为多个 IP 地址。有关别名的更多信息,请查看此处。当然,别名有单个IP地址的情况也是可以的,如下图:

$ host g.www.ms.akadns.net
g.www.ms.akadns.net is an alias for lb1.www.ms.akadns.net.
lb1.www.ms.akadns.net has address 207.46.19.190
Run Code Online (Sandbox Code Playgroud)

但是别名可以链接吗?答案是肯定的:

$ host www.microsoft.com
www.microsoft.com is an alias for toggle.www.ms.akadns.net.
toggle.www.ms.akadns.net is an alias for g.www.ms.akadns.net.
g.www.ms.akadns.net is an alias for lb1.www.ms.akadns.net.
lb1.www.ms.akadns.net has address 207.46.19.254

$ host www.google.fr
www.google.fr is an alias for www.google.com.
www.google.com is an alias for www.l.google.com.
www.l.google.com has address 74.125.39.147
www.l.google.com has address 74.125.39.103
www.l.google.com has address 74.125.39.99
www.l.google.com has address 74.125.39.106
www.l.google.com has address 74.125.39.104
www.l.google.com has address 74.125.39.105
Run Code Online (Sandbox Code Playgroud)

我没有找到任何主机名解析为不解析为 IP 地址的别名的示例,但我认为这种情况可能会发生。

不仅仅是多个 IP 地址和别名,还有其他一些特殊情况...... IPv6 怎么样?你可以试试:

$ host ipv6.google.com
ipv6.google.com is an alias for ipv6.l.google.com.
ipv6.l.google.com has IPv6 address 2a00:1450:8007::68
Run Code Online (Sandbox Code Playgroud)

其中主机名ipv6.google.com是仅限 IPv6 的主机名。双栈主机名怎么样:

$ host www.facebook.com
www.facebook.com has address 66.220.153.15
www.facebook.com has IPv6 address 2620:0:1c08:4000:face:b00c::
Run Code Online (Sandbox Code Playgroud)

再次关于 IPv6,如果您的主机仅支持 IPv4,您仍然可以解析 IPv6 地址(在仅支持 IPv4 的 WinXP 和 ipv6.google.com 上测试,您可以在 Linux 上尝试)。在这种情况下,解析成功,但 ping 失败并显示未知主机错误消息。这可能是您的脚本编写失败的情况。

我希望这些评论有用。

  • 对接受的答案的一个很好的补充,显示了一个人可能想要在脚本中处理的所有边缘情况。我的版本`host` 甚至没有说明我的盒子“有地址”。 (2认同)

小智 23

ping -q -c 1 -t 1 your_host_here | grep PING | sed -e "s/).*//" | sed -e "s/.*(//"
Run Code Online (Sandbox Code Playgroud)

无需依赖其他系统即可工作(以及在 /etc/hosts 中指定的主机)

  • ping 的使用是我所需要的,因为我需要来自主机文件的值,但 sed 模式解析正确,但这有效 ping -q -c 1 -t 1 your_host_here | grep 平| sed -e "s/^[^(]*[(]//" | sed -e "s/[)].*$//" (2认同)

小智 23

为避免别名问题并始终准备好单个 IP 地址以供使用:

python -c 'import socket; print socket.gethostbyname("www.example.com")'
Run Code Online (Sandbox Code Playgroud)

  • `python3 -c '导入套接字;打印(socket.gethostbyname(“www.example.com”))'` (2认同)

小智 23

简单但有用:

  1. getent ahostsv4 www.google.de | grep STREAM | head -n 1 | cut -d ' ' -f 1
  2. getent ahostsv6 www.google.de | grep STREAM | head -n 1 | cut -d ' ' -f 1
  3. getent hosts google.de | head -n 1 | cut -d ' ' -f 1

如果主机仍然存在,所有命令都将解析 IP 地址。如果主机指向 CNAME,它也会在这种情况下获得 IP。

第一个命令返回解析的 IPv4 地址。

第二个命令返回解析的 IPv6 地址。

第三个命令将返回所有者的首选地址,该地址可以是 IPv4 或 IPv6 地址。


小智 9

这是ping将“未知主机”考虑在内(通过通过 stderr 进行管道tr传输)并用于避免使用正则sed表达式的方法的细微变化:

ping -c1 -t1 -W0 www.example.com 2>&1 | tr -d '():' | awk '/^PING/{print $3}'
Run Code Online (Sandbox Code Playgroud)

如果捕获退出值很重要,那么以下将起作用(虽然不太优雅):

ping -c1 -t1 -W0 www.example.com &>/dev/null && ping -c1 -t1 -W0 www.example.com 2>&1 | tr -d '():' | awk '/^PING/{print $3}'
Run Code Online (Sandbox Code Playgroud)


Fra*_*nck 8

要完成 Chris Down 的回答,并解决 jfgagne 关于(可能链接的)别名的评论,这里有一个解决方案:

  • 考虑多个IP
  • 考虑一个或多个别名 (CNAME)
  • 不查询/etc/hosts文件(在我的情况下我不想要它);查询一下,dbernt的python解决方案很完美)
  • 不使用 awk/sed

    dig +short www.alias.com  | grep -v "\.$" | head -n 1
    
    Run Code Online (Sandbox Code Playgroud)

始终返回第一个 IP 地址,如果未解析,则返回空字符串。使用 dig 版本:

    $ dig -v
    DiG 9.8.1-P1
Run Code Online (Sandbox Code Playgroud)


小智 6

 php -r "echo gethostbyname('unix.stackexchange.com');"
Run Code Online (Sandbox Code Playgroud)


Dwe*_*eia 6

我希望将此添加为对 Andrew McGregor Re: ping 的评论。但是它不会让我,所以我需要添加这个作为另一个答案。(如果有人可以将其移动到评论中,请随意。)

这是另一种变体,仅使用 ping 和 grep:

ping -q -c1 -t1 your_host_here | grep -Eo "([0-9]+\.?){4}"
Run Code Online (Sandbox Code Playgroud)

grep -E用于扩展正则表达式并 grep -o仅返回匹配部分。正则表达式本身查找一位或多位数字 ( [0-9]+) 和可选的点 ( \.?) 四次 ( {4})


Mat*_*teo 5

你可以使用host

hostname=example.org

# strips the IP
IP=$( host ${hostname} | sed -e "s/.*\ //" )

# checks for errors
if [ $? -ne 0 ] ; then
   echo "Error: cannot resolve ${hostname}" 1>&2
   exit 1;
fi
Run Code Online (Sandbox Code Playgroud)


Rub*_*ONO 5

这是我使用其他人的答案编写的 Bash 食谱 - 首先尝试/etc/hosts,然后回退到 nslookup:

resolveip(){
    local host="$1"
    if [ -z "$host" ]
    then
        return 1
    else
        local ip=$( getent hosts "$host" | awk '{print $1}' )
        if [ -z "$ip" ] 
        then
            ip=$( dig +short "$host" )
            if [ -z "$ip" ]
            then
                echo "unable to resolve '$host'" >&2 
                return 1
            else
                echo "$ip"
                return 0
            fi
        else
            echo "$ip"
            return 0
        fi
    fi
}
Run Code Online (Sandbox Code Playgroud)


Phi*_*oud 5

nmap -sP 192.168.178.0/24|grep YOUR_HOSTNAME|sed -n 's/.*[(]\([0-9\.]*\)[)].*/\1/p'
Run Code Online (Sandbox Code Playgroud)

是我在没有 DNS 服务器的情况下找到的解决方案


小智 5

dig  +noall +answer +nocomments example.com | awk '{printf "%-36s\t%s\n", $1, $5 }'
Run Code Online (Sandbox Code Playgroud)


小智 5

dig 太慢,nslookup 快得多

nslookup google.com | grep -Po 'Address:\s*[0-9.]+' | tail -1 | sed -e 's/Address:\s*//g'
Run Code Online (Sandbox Code Playgroud)