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


416

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


21
可耻的是getent <ahosts|ahostsv4|ahostsv6|hosts> <hostname>答案在底部附近的某个地方。它是最简单的,不需要额外的程序包,并且也更易于从Bash脚本进行解析。
0xC0000022L 2015年

1
@ 0xC0000022L:新耻辱的是,这个问题的答案表明getent hosts somehost,当运行此同时somehost会产生一个IPv6地址,这是如何大多数其他工具(不同的pingssh至少)解析名称,并打破一些东西。使用ahosts代替hosts
j_random_hacker

@j_random_hacker:谁阻止您具体请求IPv4(ahostsv4 IPv6(ahostsv6)地址?我个人认为返回IPv6的非特定请求没有问题。您的代码应该准备好了。IPv6已经存在了20多年了。
0xC0000022L

@ 0xC0000022L:没有人“ hosts吓我一跳”,但是答案明确地暗示了这一点,到目前为止,已有4个人赞成vinc17的评论,表达了由“突然的IP​​v6”引起的痛苦。为IPv6做好准备并不总是问题:许多程序需要一种确定两个名称/地址是否引用同一主机的方法。他们可以使用简单的字符串匹配,或者必须对网络了解很多,才能找到“真实”的答案。后者是一个雷区,许多第三方程序和系统(我无法控制)都使用前者。
j_random_hacker

Answers:


533

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

getent hosts unix.stackexchange.com | awk '{ print $1 }'

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

dig +short unix.stackexchange.com

如果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 }'

如果您只想打印一个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 }'

2
默认情况下,使用dig仅适用于ipv4,其中主机同时给出ipv4和ipv6答案。这可能是意外的。你可以试试host www.google.comdig +short www.google.comhost ipv6.google.comdig +short ipv6.google.comhost www.facebook.comdig +short www.facebook.com
jfg956 2011年

6
DIG不起作用,如果是CNAME,则不会返回IP。
索林2014年

4
有时,host可以超时并且什么也不返回。对于某些域,dig +short可能在第一行中返回域别名。因此,要确保输出是IPv4地址,请使用dig +short example.com | grep -Eo '[0-9\.]{7,15}' | head -1
caiguanhao 2014年

9
使用getent hosts <host>是不正确的,例如,它可能会提供一个IPv6地址,而IPv6无法正常工作。正确的解决方案是根据getent ahosts <host>需要尝试同时使用IPv6和IPv4。
vinc17

5
值得一提的是:host,dig和nslookup似乎直接与resolv.conf中列出的服务器通信,而“ getent主机”(如果启用)同时尊重本地主机文件和库级缓存(例如nscd)。
Saustrup

141

随着hostdnsutils包:

$ host unix.stackexchange.com
unix.stackexchange.com has address 64.34.119.12

(根据注释更正了软件包名称。请注意,其他发行版包含host在不同的软件包中:Debian / Ubuntu bind9-host,openSUSE bind-utils,Frugalware bind。)


4
您是说dnsutils吗?无论如何,都host很好,谢谢
Eugene Yarmash 2011年

你也许是对的。我没有拱门在这里检查。(本意是在稍后提及此事时添加评论,但答案已经被接受,所以我想我
已将

1
如果您需要解析DNS中不存在的内容(例如/ etc / hosts),请参见下面的resolveip条目
Gavin Brock 2012年

2
请注意,host有时会返回多行输出(在重定向的情况下),host unix.stackexchange.com | tail -n1如果您只想要带有IP地址的行,则需要这样做。
爱德华·科菲

3
这个答案值得认真的否决。host是DNS工具(类似于nslookup),因此它仅在DNS中查找主机,而不在例如中查找主机/etc/hosts。因此,这不是对OP问题的答案。
peterh 2014年

54

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

resolveip -s unix.stackexchange.com
64.34.119.12

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

resolveip -s unix.stackexchange.coma
resolveip: Unable to find hostid for 'unix.stackexchange.coma': host not found
exit 2

更新 在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

我想这会为您的脚本创建一个奇怪的依赖关系...


6
这似乎是这里唯一使用操作系统的内置解析程序的解决方案-因此适用于/ etc / hosts和DNS。
加文·布罗克

8
getent,如另一个答案中所述,它也查看/ etc / hosts,并且带有glibc,因此它对Linux系统没有依赖性。
Asfand Qazi

44

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

$ dig +short unix.stackexchange.com
64.34.119.12

digdnsutils包装中也包括。


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

hostname=unix.stackexchange.com

ip=`dig +short $hostname`

if [ -n "$ip" ]; then
    echo IP: $ip
else
    echo Could not resolve hostname.
fi

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


2
请注意,如果域具有CNAME条目,则其域可能会打印在第一行而不是IP地址。
pcworld

40
getent hosts unix.stackexchange.com | cut -d' ' -f1

4
也可以考虑ahostsahostsv4ahostsv6getent
0xC0000022L 2015年

cut不适用于用于\t分隔列的getent 。在Solaris上就是这种情况。
2013年

1
@ceving:在Solaris上,您可能必须cut不带运行-d(默认\t为delimiter)。在Linux上,它是空格,因此上面的行适用。
sborsky '16

27

到目前为止,给出的解决方案通常在较简单的情况下起作用:主机名直接解析为单个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

但是什么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

因此,www.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

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

$ 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

我没有找到主机名解析为别名而不解析为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

其中主机名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::

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

我希望这些评论是有用的。


2
这是对已接受答案的很好补充,它显示了脚本编写中可能要处理的所有极端情况。我的版本host甚至没有在我的盒子上注明“有地址”。
Mihai Danila


18
ping -q -c 1 -t 1 your_host_here | grep PING | sed -e "s/).*//" | sed -e "s/.*(//"

可以不依赖于其他系统(和/ etc / hosts中指定的主机)工作


2
使用ping是我需要的,因为我需要从hosts文件中获取值,但是sed模式可以正确解析,但是可以正常运行ping -q -c 1 -t 1 your_host_here | grep Ping | sed -e“ s / ^ [^(] * [(] //” | sed -e“ s /[)].*$//”
ManiacZX 2013年

1
要解决我的家庭网络上的某些问题(例如myhostname.local),此方法有效,因此对我来说,这是最佳答案。
马特·弗里德曼

1
我也可以提出以下建议:ping -q -c 1 -t 1 bahface.local | grep -m 1 PING | cut -d "(" -f2 | cut -d ")" -f1
马特·弗里德曼

getent <ahosts|ahostsv4|ahostsv6|hosts> <hostname>适用于声明中/etc/hosts,太......,它是各种系统数据库(密码,组,别名,服务)去到的工具。
0xC0000022L

17

简单但有用:

  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地址。


到目前为止,最简单的一种。并且默认情况下可用。不像那样host,需要安装bindutils
michaelbn

7

这是一种略有变化的ping方法,该方法考虑了“未知主机”(通过stderr进行管道tr传输),并用于避免使用正则sed表达式:

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

如果捕获出口值很重要,则可以使用以下方法(尽管不太优雅):

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

我喜欢这个解决方案,因为它不需要任何额外的工具即可工作。
Radon8472 '18年

7

为了完成Chris Down的回答,并解决jfgagne关于(可能是链接的)别名的评论,以下是一种解决方案:

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

    dig +short www.alias.com  | grep -v "\.$" | head -n 1

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

    $ dig -v
    DiG 9.8.1-P1

1
谢谢,其他答案都假定“ dig + short” 总是返回一个IP地址。他们没有考虑CNAME。
jamshid


5

我希望将此添加为对Andrew McGregor Re:ping的评论。但是,它不允许我这样做,因此我需要将其添加为另一个答案。(如果有人可以将其添加为评论,请随意。)

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

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

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


4

您可以使用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

4

这是我使用其他人的答案制作的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
}

需要明确的是,getent hosts不只是在/ etc / hosts中查找-这是一个完整的DNS解析调用gethostbyaddr(3) ,这是非常不可能在的情况下,失败dig将会成功。有关getent的信息,请参见手册页
Stuart P. Bentley

@Stuart是正确的-自编写该书以来,我学到了很多东西,并且简化了一个强大的命令。getent仍然是我的最爱,尽管我也喜欢dig +short
RubyTuesdayDONO 2014年


3

也许不是最简洁,但是它似乎是健壮和高效的:

# $(get_host_dns_short "google.com")
#
# Outputs the IPv4 IP Address of a hostname, resolved by DNS. Returns 0 if DNS
# responded successfully; 1 otherwise. Will mask error output.
function get_host_dns_short()
{
    (
        set -o pipefail

        host -4 -W1 -t A "$1" 2>/dev/null | awk '/has address/ { print $NF; exit }'
    ) && return 0 || return 1
}

这将输出单个IPv4 IP,并1在发生故障时返回,同时屏蔽stderr输出。

您可以像这样使用它:

GOOGLE_IP="$(get_host_dns_short "google.com")"
if [[ $? -eq 0 ]]; then
    echo "Google's IP is ${GOOGLE_IP}."
else
    echo "Failed to resolve Google's IP."
fi

Google的IP是216.58.192.46。

如果您想要一个IPv6地址,只需将替换-4-6


3

dig +noall +answer +nocomments example.com | awk '{printf "%-36s\t%s\n", $1, $5 }'


1
在某种程度上,答案会比现有的答案有所改善。另外,请将命令缩进4个空格(请参阅markdown语法)。
maxschlepzig

2

1行解析主机名列表

for LINE in `cat ~/Desktop/mylist`; do a=$(nslookup $LINE | awk '/^Address: / { print $1 }');  echo $a >> ~/Desktop/ip; done

2

我一直在没有Mac的Mac上执行此操作getentping好像是hack。我也想考虑/etc/hosts一下。

因此,我dns.lookup为您安装了Node.js的CLI为您编写了一个愚蠢的包装程序:

$ npm install -g lookup-hostname
$ lookup google.com
62.243.192.89

在您下定决心之前,有60%的断裂几率。
dotbit

@dotbit您能详细说明吗?自从17年以来,我每周都在使用此书,从来没有任何问题。
托马斯·詹森

@Jensen,但您像往常一样是唯一的一个。我们其余的人通常都会遇到一种或另一种失败,并且总是会失败。
dotbit

“一如既往”是什么意思? “我们其余的人”是谁? “遇到失败”您看到什么具体问题?我很好奇。
托马斯·詹森


2

我不知道bash脚本的最简单方法,但是如果您要解析主机名并查看主机是否启动,请使用ping

ping -a hostname -c 1

ping主机一次并将主机名解析为IP地址。

$ ping -a www.google.com -c 1
PING www.google.com (216.58.211.132) 56(84) bytes of data.
64 bytes from arn09s10-in-f4.1e100.net (216.58.211.132): icmp_seq=1 ttl=54 time=1.51 ms

使用ping很好,因为每个人都有它,但是,如果您想在脚本中使用它,则需要从输出中过滤IP部件。
Radon8472 '18年

1

是的,已经有很多答案,但是缺少使用perl的解决方案:

perl -MSocket -MNet::hostent -E 'say inet_ntoa((gethost shift)->addr)' unix.stackexchange.com

在bash脚本中,可以这样使用:

#!/bin/bash
ipaddr=$(perl -MSocket -MNet::hostent -E 'say inet_ntoa((gethost shift)->addr)' unix.stackexchange.com)
echo $ipaddr

这里使用的模块是核心模块,因此应该随处可用,而无需与CPAN一起安装。


perl -MSocket -MNet::hostent -E 'say inet_ntoa((gethost shift)->addr)' unix.stackexchange.com 2>/dev/null 更干净。但是除了我们两个人之外,其他人都没有使用pörl,其他所有人当然都使用了Pascal Script。
dotbit

实际上,如果发生任何问题,我更喜欢查看错误消息。Can't call method "addr" on an undefined value并不是最好的错误消息,但可能会提示问题。
Slaven Rezic

1
#!/bin/bash

systemd-resolve   RT.com -t A  | awk '{ print $4 ; exit }'
systemd-resolve unix.stackexchange.com -t A --legend=no | awk '{ print $4 ; exit }'

resolveip -s      RT.com
dig       +short  RT.com
host              RT.com | awk '/has address/ { print $4 }'
nslookup          RT.com | awk '/^Address: /  { print $2 }'
ping -q -c 1 -t 1 RT.com | grep PING | sed -e "s/).*//" | sed -e "s/.*(//"

ruby     -rresolv -e      ' print    Resolv.getaddress "RT.com" '
python2  -c 'import socket; print socket.gethostbyname("RT.com")'
perl     -MSocket -MNet::hostent -E 'say inet_ntoa((gethost shift)->addr)' RT.com  2>/dev/null
php      -r "echo gethostbyname( 'RT.com' );"

echo        "   all do work for me - take your pick!  "

1
红宝石版本在IP地址周围打印引号--- print应该使用而不是p
Slaven Rezic

thx,@ Slaven Rezic,随时可以投票。然后再次,实际上,该脚本在底部可能更加可见... ;-)
dotbit

-1
host -t a cisco.com

此命令将显示ip地址(将域名重新分配到IP)


-1

除了上述解决方案之外,您还可以通过以下脚本将多个主机名转换为ip,唯一的依赖性是核心Unix中的“ ping”命令:

getip(){ ping -c 1 -t 1 $1 | head -1 | cut -d ' ' -f 3 | tr -d '()' 2>&1 | tee >> /tmp/result.log & }

getip 'hostname.number1.net'

getip 'hostname.number2.net'

getip 'hostname.number3.net'

getip 'hostname.number4.net'

getip 'hostname.number5.net'

getip 'hostname.number6.net'

getip 'hostname.number7.net'

getip 'hostname.number8.net'
$ cat /tmp/result.log

ABC.DEF.GHI.XY1

ABC.DEF.GHI.XY2

ABC.DEF.GHI.XY3

ABC.DEF.GHI.XY4

ABC.DEF.GHI.XY5

ABC.DEF.GHI.XY6

ABC.DEF.GHI.XY7

ABC.DEF.GHI.XY8
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.