¿Cómo puedo resolver un nombre de host a una dirección IP en un script Bash?


416

¿Cuál es la forma más concisa de resolver un nombre de host en una dirección IP en un script Bash? Estoy usando Arch Linux .


21
Es una pena que la getent <ahosts|ahostsv4|ahostsv6|hosts> <hostname>respuesta esté en algún lugar cerca del fondo. Es el más simple, no requiere paquetes adicionales y también es más fácil de analizar desde un script Bash.
0xC0000022L

1
@ 0xC0000022L: La nueva vergüenza es que esa respuesta sugiere que getent hosts somehost, al ejecutar esto mientras está encendidosomehost , producirá una dirección IPv6 , que es diferente de cómo la mayoría de las otras herramientas ( ping, sshal menos) resuelven nombres y rompen algunas cosas. Use el en ahostslugar de hosts.
j_random_hacker

@j_random_hacker: ¿quién le impide solicitar direcciones IPv4 ( ahostsv4) o IPv6 ( ahostsv6) específicamente ? Personalmente, no encuentro nada malo con la solicitud no específica que devuelve IPv6. Su código debe estar preparado. IPv6 ha estado disponible por más de 20 años.
0xC0000022L

@ 0xC0000022L: Nadie "me impide" hacer eso, pero la respuesta sugiere específicamente hosts, y hasta ahora 4 personas han votado por el comentario de vinc17 que expresa el dolor causado por "repentinamente IPv6". Estar preparado para IPv6 no siempre es el problema: muchos programas necesitan una forma de determinar si dos nombres / direcciones se refieren al mismo host. Pueden usar una simple coincidencia de cadenas o deben saber mucho sobre la red para encontrar la respuesta "verdadera". Este último es un campo minado, por lo que muchos programas y sistemas de terceros, sobre los que no tengo control, usan el primero.
j_random_hacker

Respuestas:


533

Puede usar getent, lo que viene con glibc(por lo que casi seguro lo tiene en Linux). Esto se resuelve usando gethostbyaddr / gethostbyname2, y también se verificará /etc/hosts/ NIS / etc:

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

O, como Heinzi dijo a continuación, puede usarlo digcon el +shortargumento (consulta los servidores DNS directamente, no mira /etc/hosts/ NSS / etc.):

dig +short unix.stackexchange.com

Si dig +shortno está disponible, cualquiera de los siguientes debería funcionar. Todos estos consultan DNS directamente e ignoran otros medios de resolución:

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

Si solo desea imprimir una IP, agregue el exitcomando al awkflujo de trabajo.

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
Por defecto, el uso de dig solo funciona con ipv4, donde el host proporciona respuestas tanto a ipv4 como a ipv6. Esto puede ser inesperado. Usted puede intentar 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

66
DIG no funciona, si es un CNAME no devolverá la IP.
sorin

44
A veces, hostpuede agotar el tiempo de espera y no devuelve nada Para algunos dominios, dig +shortpuede devolver el alias de dominio en la primera línea. Por lo tanto, para garantizar que la salida sea una dirección IPv4, use dig +short example.com | grep -Eo '[0-9\.]{7,15}' | head -1.
caiguanhao

99
El uso getent hosts <host>es incorrecto, ya que, por ejemplo, puede proporcionar una dirección IPv6 mientras IPv6 no funciona. La solución correcta es usar getent ahosts <host>para probar tanto IPv6 como IPv4 si es necesario.
vinc17

55
Vale la pena mencionar: host, dig y nslookup parece hablar directamente con los servidores enumerados en resolv.conf, mientras que "getent hosts" respetan tanto el archivo de hosts locales como el almacenamiento en caché a nivel de biblioteca (como nscd) si está habilitado.
Saustrup

141

Con hostdel paquete dnsutils :

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

(Se corrigió el nombre del paquete de acuerdo con los comentarios. Como nota, hay otras distribuciones hosten diferentes paquetes: Debian / Ubuntu bind9-host , openSUSE bind-utils , Frugalware bind ).


44
¿Quiso decir dnsutils ? De todos modos, hostfuncionó bien, gracias
Eugene Yarmash

Probablemente tengas razón. No tengo un arco aquí para verificar. (Tenía la intención de agregar un comentario más tarde mencionando esto, pero la respuesta ya había sido votada, así que supuse que lo clavé ...)
manatwork

1
Vea la entrada de resolveip a continuación si necesita resolver algo que no está en DNS (por ejemplo, / etc / hosts)
Gavin Brock

2
Tenga en cuenta que a hostveces devuelve una salida de varias líneas (en el caso de redireccionamientos), querrá host unix.stackexchange.com | tail -n1si solo desea la línea con la dirección IP.
Edward Coffey

3
Esta respuesta merece un serio voto negativo. hostes una herramienta DNS (similares a nslookup) por lo que sólo mira hacia arriba anfitriones en el DNS, no en por ejemplo /etc/hosts. Entonces NO es una respuesta a la pregunta de OP.
Peter

54

Tengo una herramienta en mi máquina que parece hacer el trabajo. La página del manual muestra que parece venir con mysql ... Así es como podría usarlo:

resolveip -s unix.stackexchange.com
64.34.119.12

El valor de retorno de esta herramienta es diferente de 0 si el nombre de host no se puede resolver:

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

ACTUALIZACIÓN En fedora, viene con 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

Supongo que crearía una dependencia extraña para tu script ...


66
Esta parece ser la única solución aquí que utiliza la resolución de compilación del sistema operativo, por lo que funciona para / etc / hosts y DNS.
Gavin Brock

8
getent, como se detalla en la otra respuesta, también analiza / etc / hosts y viene con glibc, por lo que no tiene dependencias en un sistema Linux.
Asfand Qazi

44

El siguiente comando usando le digpermite leer el resultado directamente sin ningún sed / awk / etc. magia:

$ dig +short unix.stackexchange.com
64.34.119.12

digTambién está incluido en el dnsutilspaquete.


Nota : digtiene un valor de retorno de 0, incluso si el nombre no se pudo resolver. Por lo tanto, deberá verificar si la salida está vacía en lugar de verificar el valor de retorno:

hostname=unix.stackexchange.com

ip=`dig +short $hostname`

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

Nota 2 : Si un nombre de host tiene varias direcciones IP (intente debian.org, por ejemplo), todas serán devueltas. Este "problema" afecta a todas las herramientas mencionadas en esta pregunta hasta ahora:


2
Tenga en cuenta que si un dominio tiene una entrada CNAME, su dominio puede imprimirse en la primera línea en lugar de una dirección IP.
pcworld

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

44
Ten en cuenta también ahosts, ahostsv4, ahostsv6con getent.
0xC0000022L

cutno para getent's, que se utilizan \tpara separar columnas. Este es el caso de Solaris.
ceving

1
@ceving: en Solaris, es posible que deba ejecutarse cutsin -d(predeterminado \tcomo delimitador). En Linux son espacios, por lo tanto, la línea anterior funciona.
sborsky

27

Las soluciones dadas hasta ahora funcionan principalmente en el caso más simple: el nombre de host se resuelve directamente en una sola dirección IPv4. Este podría ser el único caso en el que necesite resolver nombres de host, pero si no, a continuación se presenta una discusión sobre algunos casos que podría necesitar manejar.

Chris Down y Heinzi discutieron brevemente el caso en el que el nombre de host se resuelve en más de una dirección IP. En este caso (y otros a continuación), las secuencias de comandos básicas bajo el supuesto de que un nombre de host se resuelve directamente en una sola dirección IP pueden romperse. A continuación, un ejemplo con un nombre de host que se resuelve en más de una sola dirección 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

Pero que es www.l.google.com? Aquí es donde debe introducirse el caso de alias . Veamos el siguiente ejemplo:

$ 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

Por www.google.comlo tanto , no se resuelve directamente en direcciones IP, sino en un alias que se resuelve en varias direcciones IP. Para obtener más información sobre alias, consulte aquí . Por supuesto, el caso donde un alias tiene una sola dirección IP es posible, como se muestra a continuación:

$ 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

¿Pero se pueden encadenar alias? La respuesta es sí:

$ 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

No encontré ningún ejemplo en el que un nombre de host se resuelva en un alias que no se resuelva en una dirección IP, pero creo que el caso podría ocurrir.

Más que múltiples direcciones IP y alias, ¿hay otros casos especiales ... ¿qué pasa con IPv6? Tu podrías intentar:

$ 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

Donde el nombre de host ipv6.google.comes un nombre de host solo IPv6. ¿Qué pasa con los nombres de host de doble pila?

$ 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::

Nuevamente sobre IPv6, si su host es solo IPv4, aún puede resolver direcciones IPv6 (probado en un WinXP solo IPv4 y con ipv6.google.com, puede probarlo en Linux). En este caso, la resolución tiene éxito, pero un ping falla con un mensaje de error de host desconocido . Este podría ser un caso en el que su script falla.

Espero que esas observaciones hayan sido útiles.


2
Qué gran complemento para la respuesta aceptada, que muestra todos los casos límite que uno podría tratar en la creación de secuencias de comandos. Mi versión hostni siquiera dice "tiene dirección" para mis cajas.
Mihai Danila


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

funciona sin dependencias en otros sistemas (y para hosts especificados en / etc / hosts)


2
El uso de ping es lo que necesitaba, ya que necesito el valor del archivo hosts pero el patrón sed analizó correctamente pero esto funcionó ping -q -c 1 -t 1 your_host_here | grep PING | sed -e "s / ^ [^ (] * [(] //" | sed -e "s /[)font>.*$//"
ManiacZX

1
Para resolver algo en mi red doméstica como myhostname.local, esto funciona, así que para mí esta es la mejor respuesta.
Matt Friedman

1
¿Puedo sugerir esto también:ping -q -c 1 -t 1 bahface.local | grep -m 1 PING | cut -d "(" -f2 | cut -d ")" -f1
Matt Friedman

getent <ahosts|ahostsv4|ahostsv6|hosts> <hostname>trabaja para las declaraciones en el interior /etc/hosts, también ... y es la herramienta de go-to-para todo tipo de bases de datos del sistema (passwd, grupo, alias, servicios).
0xC0000022L

17

Simple pero útil:

  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

Todos los comandos resolverán una dirección IP si el host todavía existe. Si el host apunta a CNAME, también obtendrá la IP en ese caso.

El primer comando devuelve la dirección IPv4 resuelta

El segundo comando devuelve la dirección IPv6 resuelta

El tercer comando devolverá la dirección preferida de los propietarios a la dirección IPv4 o IPv6.


De lejos, el más simple. Y está disponible por defecto. No es que hosteso requiera la instalación debindutils
michaelbn

7

Aquí hay una ligera variación del pingenfoque que tiene en cuenta "host desconocido" (al canalizar a través de stderr) y utiliza trpara evitar el uso de sedexpresiones regulares:

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

En caso de que sea importante capturar el valor de salida, lo siguiente funcionará (aunque menos elegante):

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

Me encanta esta solución, porque funciona sin herramientas adicionales.
Radon8472

7

Para completar la respuesta de Chris Down y abordar los comentarios de jfgagne sobre los alias (posiblemente encadenados), aquí hay una solución que:

  • toma en cuenta múltiples IP
  • toma en cuenta uno o más alias (CNAME)
  • no consulta el /etc/hostsarchivo (en mi caso no lo quería); para consultarlo, la solución python de dbernt es perfecta)
  • no usa awk / sed

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

Siempre devuelve la primera dirección IP o tring vacío si no se resuelve. con versión de cavar:

    $ dig -v
    DiG 9.8.1-P1

1
Gracias, otras respuestas suponen que "dig + short" siempre devuelve una única dirección IP. No estaban contando para CNAME.
jamshid

5
 php -r "echo gethostbyname('unix.stackexchange.com');"

esto funciona, pero requiere que php esté instalado en su terminal
Radon8472

1
puede ser útil en un contenedor php docker típico donde "host", "dig", etc. no están disponibles
Fabian Schmengler

5

Me hubiera gustado agregar esto como comentario a Andrew McGregor Re: ping. Sin embargo, no me lo permitió, así que necesito agregar esto como otra respuesta. (Si alguien puede moverlo a un comentario, siéntase libre de hacerlo).

Esta es otra variante, solo usando ping y grep:

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

grep -Epara expresiones regulares extendidas y grep -opara devolver solo la parte coincidente. la expresión regular en sí busca uno o varios dígitos ( [0-9]+) y, opcionalmente, un punto ( \.?) cuatro veces ( {4})


4

Podrías usar 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

Aquí hay una receta de Bash que preparé usando las respuestas de otras personas: primero intenta /etc/hosts, luego recurre a 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
}

Para ser claros, getent hostsno es solo una búsqueda en / etc / hosts: es una llamada de resolución DNS completa a gethostbyaddr (3) , y es muy poco probable que falle en un caso en el que digtenga éxito. Vea la página del manual para getent .
Stuart P. Bentley

@Stuart tiene razón: he aprendido mucho desde que escribí eso y simplifiqué demasiado un comando poderoso. getentsigue siendo mi favorito, aunque también me gustadig +short
RubyTuesdayDONO

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

fue la solución que encontré sin servidor DNS


3

Quizás no sea el más conciso, pero parece ser robusto y eficiente:

# $(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
}

Esto generará una sola IP IPv4, así como también regresará 1en caso de falla, mientras enmascara la salida stderr.

Puedes usarlo así:

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

La IP de Google es 216.58.192.46.

Si desea una dirección IPv6, simplemente reemplácela -4con -6.


3

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


1
Algún contexto en el que esa respuesta mejora sobre las ya existentes sería genial. Además, sangra los comandos por 4 espacios (véase la sintaxis de rebajas).
maxschlepzig

2

1 línea resuelve una lista de nombre de host

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

2

Estoy haciendo esto todo el tiempo en mi Mac que no tiene getent. pingParece un truco. Me gustaría tener /etc/hostsen cuenta también.

Entonces, escribí un estúpido contenedor para dns.lookupustedes que tienen Node.js instalado para proporcionar una CLI:

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

60% de probabilidad de rotura antes de acercarse a una resolución.
dotbit

@dotbit ¿podrías elaborar? Lo he usado semanalmente desde el '17 y nunca tuve ningún problema.
Thomas Jensen

@Jensen, pero tú eres el único, como siempre. El resto de nosotros usualmente nos encontramos con FALLA de un tipo u otro, y siempre.
dotbit

"como siempre" ¿Qué quieres decir con eso? "El resto de nosotros" ¿Quién es ese? "run to FAIL" ¿Qué problema específico estás viendo? Soy curioso.
Thomas Jensen

2

cavar es demasiado lento, nslookup es mucho más rápido

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

2

No conozco la forma más fácil para un script bash, pero si desea resolver un nombre de host y ver si el host está activo, ping¡ use !

ping -a hostname -c 1

Será pingel host una vez y resolverá el nombre del host a la dirección 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

usar ping es bueno, porque todos lo tienen, pero necesita filtrar la Parte IP de las salidas, si desea usarlo en un script.
Radon8472

1

Sí, ya hay muchas respuestas, pero falta una solución con perl:

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

En un script bash se podría usar así:

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

Los módulos utilizados aquí son módulos centrales, por lo que deberían estar disponibles en todas partes sin instalar con CPAN.


perl -MSocket -MNet::hostent -E 'say inet_ntoa((gethost shift)->addr)' unix.stackexchange.com 2>/dev/null Es mucho más limpio. pero nadie más que nosotros dos estamos usando pörl, todos los demás usan Pascal Script, por supuesto.
dotbit

En realidad, prefiero ver los mensajes de error si algo sale mal. Can't call method "addr" on an undefined valueno es exactamente el mejor mensaje de error, pero puede dar una pista sobre el problema.
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
La versión ruby ​​imprime comillas alrededor de la dirección IP, probablemente printdebería usarse en lugar de p.
Slaven Rezic

Gracias, @Slaven Rezic y siéntete libre de votar. una vez más, aquí en la parte inferior el script puede ser más visible en realidad ... ;-)
dotbit

-1
host -t a cisco.com

Este comando mostrará la dirección IP (reslove el dominio a IP)


-1

Además de la solución anterior, puede traducir varios nombres de host a ip a través del script a continuación, la única dependencia es el comando "ping" en el núcleo de 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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.