【问题标题】:Portable way to resolve host name to IP address将主机名解析为 IP 地址的便携式方法
【发布时间】:2015-09-26 21:59:26
【问题描述】:

我需要在 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

【问题讨论】:

  • 如果您需要您的代码在busybox 中工作,那么它不是 bash;这意味着您需要 POSIX sh 兼容性,这是一种不同的语言(尽管 bash 是超集,就像 C++ 是 C 的超集一样)。请适当标记。
  • @CharlesDuffy 谢谢!我不知道有什么区别。

标签: shell dns sh


【解决方案1】:

我没有使用 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 避免了来自nslookup 的 Windows 版本的有关打印非权威服务器的警告。

sed 命令查找包含Name 的行,然后在删除短语Addresses: 后打印下一行,当有多个IP(v4 或6)地址时——或Address:,当只有一个地址时由名称服务器返回。

-n 选项表示不打印行,除非有 p 命令while the-r` 选项表示使用扩展正则表达式(GNU sed 是 Cygwin 和 Ubuntu 的默认值)。

【讨论】:

  • 我在 Cygwin 中试过这个。有几个问题。我会和他们一起更新 mu 问题。
  • 问题是在不同的语言版本中,消息是不同的。你不能假设某处会写成Name。无论如何,谢谢你的回答。
  • 说实话,我有Windows 的波兰语版本。有Nazwa 而不是Name。还有更多变化,像这样。
  • 我更新了我的Cygwin,它现在使用nslooup 的unix 版本。
  • @NO_NAME 哇!我没有考虑到!您可以将 sed 扩展正则表达式修改为 /Name|Nazwa/ 以在其他语言环境中工作。你的问题总是越来越难。 :)
【解决方案2】:

如果您想在几乎所有现代 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 版本(例如 Git Bash,使用 mingw 构建,其中进程替换不起作用),那么您可以改用:

address=$(dig +short google.com | grep -E '^[0-9.]+$' | head -n 1)

digbind-utils 包中可用于cygwin;由于bind 是 UNIX 上使用最广泛的 DNS 服务器,bind-utils(由相同的代码库构建)也可用于几乎所有 Unix 系列操作系统。

【讨论】:

  • 我希望命令默认可用。这对我来说不是问题,但脚本的其他用户可能会抱怨。不过,这很简单,所以我喜欢这个。
  • @NO_NAME,你认为什么“默认可用”?如果我提供了一个使用它的单行器,Python 是否有效?
  • 这个问题比我最初想象的要复杂得多。当主机名是 A 记录时,此答案非常有效。但是,如果主机名是 CNAME,则dig +short 打印的第一行是规范名称。
  • 是的,但是对于每个操作系统和发行版来说,开箱即用或不开箱即用是不同的,这就是为什么我需要问:对于你关心的平台,Python 是否可用默认情况下?
  • @AnthonyGeoghegan,很好,谢谢。适当地调整(尽管有一个丑陋的黑客)。
【解决方案3】:

TL;DR; 选项 2 是我对 IPv4 地址的首选。调整正则表达式以获取 IPv6 和/或 awk 以获取两者。对 EDIT

中给出的选项 2 建议用法稍作修改

这是一个非常晚的答案,但我想我会在这里分享我的解决方案,尤其是。因为接受的答案在 openWRT 上对我不起作用(没有最小设置的 python)和 other answer 错误“逗号后找不到地址”。

选项 1(给出名称服务器发送的最后一个条目的最后一个地址):

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

非常简单直接,不需要解释管道命令。

虽然,在我的测试中,这总是给出 IPv4 地址(因为 IPv4 总是在最后一行,至少在我的测试中是这样。)但是,我读到了 nslookup 的意外行为。所以,我必须找到一种方法来确保即使顺序颠倒我也能获得 IPv4 - 谢谢regex

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

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

解释:

nslookup example.com 2&gt;/dev/null - 查找给定主机并忽略 STDERR (2&gt;/dev/null)

sed 's/[^0-9. ]//g' - 获取 IPv4 的正则表达式(数字和点,阅读关于 's' 命令here

tail -n 1 - 获取最后 1 行(alt,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 命令的输出。
【解决方案4】:

这是我从早期答案中窃取的变体:

nslookup blueboard 2&gt; /dev/null | awk '/Address/{a=$3}END{print a}'

这取决于 nslookup 返回的匹配行,如下所示:

Address 1: 192.168.1.100 blueboard

...并且只返回最后一个地址。

注意事项:这根本不处理不匹配的主机名。

【讨论】:

  • 当输出为Address: 192...Addresses: ... 时匹配不起作用(如问题所示)。
  • 可以改进以处理非匹配主机名:awk '/Name/ {inName=1} inName&amp;&amp;/Address/ {print $3; exit}'(仅打印第一个匹配项。)
猜你喜欢
  • 2011-03-12
  • 2013-01-29
  • 2011-05-07
  • 2019-09-29
  • 2013-03-25
  • 1970-01-01
  • 2014-10-13
  • 2021-10-08
  • 1970-01-01
相关资源
最近更新 更多