Как мне разрешить имя хоста в IP-адрес в скрипте Bash?


416

Какой самый краткий способ преобразовать имя хоста в IP-адрес в скрипте Bash? Я использую Arch Linux .


21
Позор, что getent <ahosts|ahostsv4|ahostsv6|hosts> <hostname>ответ где-то там, внизу. Он самый простой, не требует дополнительных пакетов и его легче анализировать из скрипта Bash.
0xC0000022L

1
@ 0xC0000022L: Новый позор в том , что этот ответ предполагает getent hosts somehost, когда работает это время наsomehost произведет IPv6 - адрес , который отличается от того, как и большинства других инструментов ( ping, sshпо крайней мере) разрешения имен, и ломает некоторые вещи. Используйте ahostsвместо hosts.
j_random_hacker

@j_random_hacker: кто мешает вам запрашивать конкретно адреса IPv4 ( ahostsv4) или IPv6 ( ahostsv6)? Лично я не нахожу ничего плохого в неопределенном запросе, возвращающем IPv6. Ваш код должен быть подготовлен. IPv6 существует уже более 20 лет.
0xC0000022L

@ 0xC0000022L: Никто не «мешает мне» сделать это, но ответ определенно подсказывает hosts, и до сих пор 4 человека проголосовали против комментария vinc17, выражающего боль, вызванную «неожиданно IPv6». Подготовка к IPv6 не всегда является проблемой: многим программам нужен способ определить, относятся ли два имени / адреса к одному и тому же хосту. Они могут либо использовать простое сопоставление строк, либо они должны много знать о сети, чтобы найти «верный» ответ. Последнее является минным полем, поэтому многие сторонние программы и системы, которые я не могу контролировать, используют первые.
j_random_hacker

Ответы:


533

Вы можете использовать getent, что идет с glibc(так что вы почти наверняка имеете его в Linux). Это решается с помощью gethostbyaddr / gethostbyname2, а также проверяет /etc/hosts/ NIS / etc:

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

Или, как сказал Хайнци ниже, вы можете использовать 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, где host дает ответы как 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.
jfg956

6
DIG не работает, если это CNAME, он не вернет IP.
сорин

4
Иногда hostможет быть тайм-аут и ничего не возвращает. Для некоторых доменов dig +shortможет возвращать псевдоним домена в первой строке. Итак, чтобы убедиться, что выводом является адрес IPv4, используйте dig +short example.com | grep -Eo '[0-9\.]{7,15}' | head -1.
caiguanhao

9
Использование getent hosts <host>некорректно, например, оно может дать адрес IPv6, а IPv6 не работает. Правильное решение - использовать getent ahosts <host>оба IPv6 и IPv4, если это необходимо.
vinc17

5
Стоит упомянуть: кажется, что host, dig и nslookup напрямую взаимодействуют с серверами, перечисленными в resolv.conf, тогда как «getent hosts» уважают как локальный файл hosts, так и кэширование на уровне библиотеки (например, nscd), если оно включено.
Saustrup

141

С hostиз пакета dnsutils :

$ 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работал хорошо, спасибо
Евгений Ярмаш

Возможно ты прав. У меня нет Арка здесь, чтобы проверить. (Предполагается добавить комментарий, в котором позже упоминается об этом, но за ответ уже проголосовали, поэтому я предположил, что прибил его ...)
manatwork

1
Посмотрите запись resolip ниже, если вам нужно разрешить что-то не в DNS (например, / etc / hosts)
Гэвин Брок

2
Имейте hostв виду, что иногда возвращает многострочный вывод (в случае перенаправления), вы захотите, host unix.stackexchange.com | tail -n1если вы просто хотите строку с IP-адресом.
Эдвард Коффи

3
Этот ответ заслуживает серьезного снижения. hostявляется инструментом DNS (аналогично nslookup), поэтому он ищет хосты только в DNS, а не, например, в /etc/hosts. Так что это НЕ ответ на вопрос ОП.
Петр

54

У меня есть инструмент на моей машине, который, кажется, делает эту работу. Страница man показывает, что, похоже, она поставляется с 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-сервером:

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.
Асфанд Кази

44

Следующая команда using digпозволяет вам читать результат напрямую без каких-либо sed / awk / etc. магия:

$ dig +short unix.stackexchange.com
64.34.119.12

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

Примечание 2 : Если имя хоста имеет несколько IP-адресов (попробуйте debian.org, например), все они будут возвращены. Эта «проблема» затрагивает все инструменты, упомянутые в этом вопросе:


2
Обратите внимание, что если в домене есть запись CNAME, его домен может быть напечатан в первой строке вместо IP-адреса.
pcworld

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

4
Также рассмотрим ahosts, ahostsv4, ahostsv6с getent.
0xC0000022L

cutне для гетентов, которые используют \tдля разделения столбцов. Это дело Соляриса.
ceving

1
@ceving: В Solaris вам, возможно, придется работать cutбез -d(по умолчанию \tэто разделитель). В Linux это пробелы, поэтому строка выше работает.
сборный

27

Представленные решения в основном работают в более простом случае: имя хоста напрямую разрешается в один адрес IPv4. Это может быть единственный случай, когда вам нужно разрешить имена хостов, но если нет, то ниже приводится обсуждение некоторых случаев, которые вам, возможно, придется обработать.

Крис Даун и Хайнци кратко обсудили случай, когда имя хоста разрешается для более чем одного 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 (протестировано на WinXP только для IPv4, а с ipv6.google.com вы можете попробовать его в Linux). В этом случае разрешение завершается успешно, но проверка связи не выполняется с неизвестным сообщением об ошибке хоста . Это может быть случай, когда ваш сценарий не работает.

Я надеюсь, что эти замечания были полезны.


2
Какое отличное дополнение к принятому ответу, показывающее все крайние случаи, с которыми можно столкнуться при написании сценариев. В моей версии hostдаже нет «адреса» для моих ящиков.
Михай Данила


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

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тоже ... и это инструмент идти к для всех видов системных баз данных (PASSWD, группы, псевдонимов, услуги).
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

7

Чтобы завершить ответ Криса Дауна и обратиться к комментариям jfgagne относительно (возможно, связанных) псевдонимов, вот решение, которое:

  • учитывает несколько IP-адресов
  • учитывает один или несколько псевдонимов (CNAME)
  • не запрашивает /etc/hostsфайл (в моем случае я этого не хотел); чтобы запросить его, решение Python от dbernt идеально подходит)
  • не использует awk / sed

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

Всегда возвращает первый IP-адрес или пустую строку, если не решен. с версией раскопок:

    $ dig -v
    DiG 9.8.1-P1

1
Спасибо, другие ответы предполагают, что "dig + short" всегда возвращает один IP-адрес. Они не учитывали CNAME.
Джамшид


5

Я хотел бы добавить это как комментарий к Эндрю МакГрегору 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это не просто поиск в / и т.д. / хостов - это полный на DNS-разрешения вызова gethostbyaddr (3) , и это очень маловероятно , чтобы потерпеть неудачу в случае , если digудастся. Смотрите man-страницу для getent .
Стюарт П. Бентли

@Stuart прав - я многому научился с тех пор, как написал это, и упростил мощную команду. getentостается моим любимым, хотя мне тоже нравитсяdig +short
RubyTuesdayDONO

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

было решение, которое я нашел без DNS-сервера


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

IP-адрес Google - 216.58.192.46.

Если вы хотите вместо этого адрес IPv6, просто замените -4на -6.


3

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


1
Некоторый контекст о том, как этот ответ улучшится по сравнению с уже существующими, будет отличным. Также, пожалуйста, вставьте команды в 4 пробела (см. Синтаксис уценки).
maxschlepzig

2

1 строка разрешает список имен хостов

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

2

Я делаю это все время на моем Mac, которого нет getent. pingпохоже на взлом. Я хотел бы принять /etc/hostsво внимание также.

Итак, я написал глупую оболочку для dns.lookupвас, у которых установлен Node.js для предоставления CLI:

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

60% вероятности поломки до того, как вы приблизитесь к разрешению.
dotbit

@dotbit не могли бы вы уточнить? Я использовал этот еженедельник с 17 года и никогда не было проблем.
Томас Дженсен

@ Дженсен, но ты, как всегда, единственный. Остальные из нас обычно сталкиваются с FAIL того или иного рода, и всегда.
дотбит

"как всегда", что вы подразумеваете под этим? "Остальные из нас" Кто это? "столкнуться с ошибкой" Какую конкретную проблему вы видите? Мне любопытно.
Томас Дженсен

2

копать слишком медленно, nslookup намного быстрее

nslookup google.com | grep -Po 'Address:\s*[0-9.]+' | tail -1 | sed -e 's/Address:\s*//g'

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

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не совсем лучшее сообщение об ошибке, но может дать подсказку о проблеме.
Раб Резиц

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
Версия ruby ​​печатает кавычки вокруг IP-адреса - вероятно, printследует использовать вместо p.
Раб Резич

THX, @Slaven Rezic и не стесняйтесь upvote. опять же, здесь внизу скрипт может быть более видимым на самом деле ... ;-)
dotbit

-1
host -t a cisco.com

эта команда покажет IP-адрес (перенастроит домен на IP)


-1

Помимо вышеприведенного решения, вы можете перевести несколько имен хостов в ip через скрипт ниже, единственная зависимость - это команда «ping» в ядре Unix:

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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.