可移植的方法来将主机名解析为IP地址

12

我需要在一个shell脚本中将主机名解析为IP地址。该代码必须至少能在 CygwinUbuntuOpenWrt(busybox) 上工作。假定每个主机只有一个IP地址。

示例:

  • 输入

google.com
  • 输出

  • 216.58.209.46
    

    编辑:nslookup可能看起来是一个不错的解决方案,但它的输出结果相当不可预测且难以过滤。以下是我电脑上(Cygwin)执行该命令的结果:

    >nslookup google.com
    Unauthorized answer:
    Serwer:  UnKnown
    Address:  fdc9:d7b9:6c62::1
    
    Name:   google.com
    Addresses:  2a00:1450:401b:800::200e
              216.58.209.78
    

    4
    如果您需要在 busybox 中运行您的代码,那么它并不是 bash;这意味着您需要 POSIX sh 兼容性,这是一种不同的语言(尽管 bash 是其超集,就像 C++ 是 C 的超集一样)。请适当标记。 - Charles Duffy
    1
    @CharlesDuffy 谢谢!我不知道这个区别。 - Piotr Siupa
    4个回答

    7
    我没有使用OpenWRT或Busybox的经验,但是以下一行命令可以在Cygwin或Ubuntu的基本安装中使用:
    ipaddress=$(LC_ALL=C nslookup $host 2>/dev/null  | sed -nr '/Name/,+1s|Address(es)?: *||p')
    

    上述方法适用于Ubuntu和Windows版本的nslookup。但是,只有当DNS服务器回复一个IP地址(v4或v6)时才起作用;如果返回多个地址,则使用第一个地址。

    解释

    LC_ALL=C nslookup在运行nslookup命令时设置LC_ALL环境变量,以便该命令忽略当前系统语言环境,并以命令的默认语言(英语)打印其输出。 2>/dev/null避免在Windows版本的nslookup中打印有关非授权服务器的警告。
    sed命令查找包含“Name”的行,然后在存在多个IP(v4或6)地址时,在去除短语“Addresses:”后打印以下行,否则在名称服务器仅返回一个地址时打印“Address:”。 -n选项表示只有在有p命令时才会打印行,而-r选项表示使用扩展正则表达式(Cygwin和Ubuntu的默认值为GNU sed)。

    我在Cygwin中尝试了这个。有一些问题。我会在我的问题中更新它们。 - Piotr Siupa
    问题在于不同语言版本的消息是不同的。你不能假设某个地方会写Name。无论如何,感谢您的回答。 - Piotr Siupa
    我更新了我的 Cygwin,现在它使用的是 Unix 版本的 nslookup - Piotr Siupa
    更容易的方法是在命令范围内设置 LC_ALL=C(例如 LC_ALL=C nslookup),从而忽略当前系统区域设置。 - Charles Duffy
    好想法,@CharlesDuffy。我已经编辑了我的答案,包括你的建议。谢谢。顺便说一下,自从回答这个问题以来,我更喜欢使用dig(如你的答案)而不是nslookup - Anthony Geoghegan
    显示剩余3条评论

    4

    如果你想在几乎任何现代UNIX系统上使用开箱即用的工具,那么选择Python:

    pylookup() {
      python -c 'import socket, sys; print socket.gethostbyname(sys.argv[1])' "$@" 2>/dev/null
    }
    
    address=$(pylookup google.com)
    

    对于专用工具而言,dignslookup 更易于使用,它的short模式仅输出文字回答 -- 在这种情况下是 IP 地址。如果发现有多个地址,请只选择第一个:

    # this is a bash-specific idiom
    read -r address < <(dig +short google.com | grep -E '^[0-9.]+$')
    

    如果您需要使用 POSIX sh 或损坏的 bash 版本(例如使用 mingw 构建的 Git Bash,其中进程替换不起作用),则可以使用以下命令:
    address=$(dig +short google.com | grep -E '^[0-9.]+$' | head -n 1)
    

    dig 命令可以在 bind-utils 软件包中找到,该软件包可在 cygwin 中使用;由于 bind 是 UNIX 上最广泛使用的 DNS 服务器,因此基于相同代码库构建的 bind-utils 软件包也可用于几乎所有的 Unix 家族操作系统。


    1
    @NO_NAME,你认为“默认可用”是什么意思?如果我提供了一个使用Python的一行代码,它能工作吗? - Charles Duffy
    1
    这个问题比我最初想象的要棘手得多。当主机名是一个A记录时,这个答案的效果非常完美。然而,如果主机名是一个CNAME,则由dig +short打印的第一行是规范名称。 - Anthony Geoghegan
    “默认可用” - 这意味着您无需安装任何未默认选择的软件包。 - Piotr Siupa
    2
    是的,但是每个操作系统和发行版的默认安装内容都不同,这就是为什么我需要问一下:对于您关心的平台,Python是否可以默认使用? - Charles Duffy
    1
    @AnthonyGeoghegan,发现得好,谢谢。已经适当调整(虽然有一个丑陋的hack)。 - Charles Duffy
    显示剩余7条评论

    3
    这里是一个结合以前答案的变体:
    nslookup blueboard 2> /dev/null | awk '/Address/{a=$3}END{print a}'
    这依赖于nslookup返回匹配的行,看起来像这样:
    Address 1: 192.168.1.100 blueboard
    ...并且只返回最后一个地址。
    注意:这完全不处理不匹配的主机名。

    当输出为Address: 192...Addresses: ...时(如问题中所示),匹配不起作用。 - simohe
    1
    可以改进以处理非匹配的主机名:awk '/Name/ {inName=1} inName&&/Address/ {print $3; exit}'(这只打印第一个匹配项。) - simohe

    3
    TL;DR; 对于IPv4地址,我更喜欢选项2。调整正则表达式以获取IPv6和/或awk来获取两者。针对EDIT中提出的建议,稍作修改就可以使用选项2。

    虽然我的回答来得非常晚,但是我想在这里分享我的解决方案,特别是因为已接受的答案在openWRT上无法使用(最小安装没有Python),而其他答案在逗号后面找不到地址而出现错误。

    选项1(从Nameserver发送的最后一个条目中提供最后一个地址):

    nslookup example.com 2>/dev/null | tail -2 | tail -1 | awk '{print $3}'
    

    非常简单明了,不需要解释管道命令。

    不过,在我的测试中,这总是返回IPv4地址(因为IPv4始终是最后一行,至少在我的测试中是)。然而,我读到了关于nslookup的意外行为。因此,我必须找到一种方法来确保即使顺序相反,我也能获得IPv4 - 感谢正则表达式

    选项2(确保您获得IPv4):

    nslookup example.com 2>/dev/null | sed 's/[^0-9. ]//g' | tail -n 1 | awk -F " " '{print $2}'
    

    解释:

    nslookup example.com 2>/dev/null - 查询给定主机并忽略 STDERR (2>/dev/null)

    sed 's/[^0-9. ]//g' - 正则表达式获取 IPv4 (数字和点, 可以在这里了解 's' 命令 here)

    tail -n 1 - 获取最后 1 行 (或者使用 tail -1)

    awk -F " " '{print $2} - 使用 " " 作为字段分隔符,捕获并打印行的第二部分

    编辑: 根据评论稍作修改,使其更通用:

    nslookup example.com 2>/dev/null | printf "%s" "$(sed 's/[^0-9. ]//g')" | tail -n 1 | printf "%s" "$(awk -F " " '{print $1}')"
    

    在上面的编辑中,我使用了printf命令替换来处理任何不需要的尾随换行符。

    您的回答基于一堆只在OpenWRT上才成立的假设,这完全不具备可移植性。请查看我在问题中粘贴的nslookup命令的输出结果。 - Piotr Siupa

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