Como posso resolver um nome de host para um endereço IP em um script Bash?


416

Qual é a maneira mais concisa de resolver um nome de host para um endereço IP em um script Bash? Estou usando o Arch Linux .


21
Pena que a getent <ahosts|ahostsv4|ahostsv6|hosts> <hostname>resposta esteja em algum lugar lá embaixo, perto do fundo. É o mais simples, não requer pacotes extras e é mais fácil analisar também a partir de um script Bash.
0xC0000022L

1
@ 0xC0000022L: O novo vergonha é que essa resposta sugere getent hosts somehost, quando executar este enquanto emsomehost produzirá um endereço IPv6 , que é diferente de como a maioria das outras ferramentas ( ping, sshpelo menos) resolver nomes, e quebra algumas coisas. Use o em ahostsvez de hosts.
Jrandom_hacker

@j_random_hacker: quem impede você de solicitar especificamente endereços IPv4 ( ahostsv4) ou IPv6 ( ahostsv6)? Pessoalmente, não encontro nada errado com a solicitação inespecífica que retorna o IPv6. Seu código deve estar preparado. O IPv6 está presente há mais de 20 anos.
0xC0000022L

@ 0xC0000022L: Ninguém "me impede" de fazer isso, mas a resposta sugere especificamente hosts, e até agora 4 pessoas votaram positivamente no comentário do vinc17 expressando a dor causada pelo "repente IPv6". Estar preparado para o IPv6 nem sempre é o problema: muitos programas precisam de uma maneira de determinar se dois nomes / endereços se referem ao mesmo host. Eles podem usar a correspondência simples de cadeias de caracteres ou precisam saber muito sobre a rede para encontrar a resposta "verdadeira". O último é um campo minado, tantos programas e sistemas de terceiros - que eu não tenho controle - usam o primeiro.
Jrandom_hacker

Respostas:


533

Você pode usar o getentque vem com glibc(então você quase certamente o possui no Linux). Isso resolve usando gethostbyaddr / gethostbyname2 e também verifica /etc/hosts/ NIS / etc:

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

Ou, como Heinzi disse abaixo, você pode usar digo +shortargumento (consulta diretamente os servidores DNS, não consulta /etc/hosts/ NSS / etc):

dig +short unix.stackexchange.com

Se dig +shortnão estiver disponível, qualquer um dos seguintes deve funcionar. Todos eles consultam o DNS diretamente e ignoram outros meios de resolução:

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

Se você deseja imprimir apenas um IP, adicione o exitcomando ao awkfluxo de trabalho do.

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 padrão, o uso de dig funciona apenas com ipv4, em que o host fornece respostas para ipv4 e ipv6. Isso pode ser inesperado. Você pode tentar 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 não funciona, se for um CNAME, ele não retornará o IP.
22414

4
Às vezes, hostpode ser atingido o tempo limite e não retorna nada. Para alguns domínios, dig +shortpode retornar o alias do domínio na primeira linha. Portanto, para garantir que a saída seja um endereço IPv4, use dig +short example.com | grep -Eo '[0-9\.]{7,15}' | head -1.
caiguanhao

9
O uso getent hosts <host>está incorreto, pois, por exemplo, ele pode fornecer um endereço IPv6 enquanto o IPv6 não funciona. A solução correta é usar getent ahosts <host>para tentar o IPv6 e o ​​IPv4, se necessário.
precisa saber é

5
Vale mencionar: host, dig e nslookup parecem conversar diretamente com os servidores listados no resolv.conf, enquanto "getent hosts" respeita o arquivo de hosts locais e o cache no nível da biblioteca (como o nscd), se ativado.
Saustrup

141

Com hosto pacote dnsutils :

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

( Nome do pacote corrigido de acordo com os comentários. Como nota, outras distribuições têm hostem pacotes diferentes: Deb9 / Ubuntu bind9-host , openSUSE bind-utils , Frugalware bind .)


4
Você quis dizer dnsutils ? Enfim, hostfuncionou bem, obrigado
Eugene Yarmash

Você provavelmente está certo. Não tenho nenhum Arch aqui para verificar. (Destinado para acrescentar um comentário depois de mencionar isso, mas a resposta já foi upvoted então eu acho que eu pregado ...)
manatwork

1
Veja a entrada resolveip abaixo se você precisa para resolver algo não no DNS (por exemplo, / etc / hosts)
Gavin Brock

2
Esteja ciente de que hostàs vezes retorna saída com várias linhas (no caso de redirecionamentos), você desejará host unix.stackexchange.com | tail -n1se quiser apenas a linha com o endereço IP.
Edward Coffey

3
Esta resposta merece um voto negativo sério. hosté uma ferramenta de DNS (semelhante a nslookup), portanto, apenas procura hosts no DNS, não no por exemplo /etc/hosts. Portanto, NÃO é uma resposta para a pergunta do OP.
peterh 11/09/14

54

Eu tenho uma ferramenta na minha máquina que parece fazer o trabalho. A página de manual mostra que parece vir com o mysql ... Aqui está como você pode usá-lo:

resolveip -s unix.stackexchange.com
64.34.119.12

O valor de retorno desta ferramenta é diferente de 0 se o nome do host não puder ser resolvido:

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

ATUALIZAÇÃO No fedora, ele vem com o 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

Eu acho que isso criaria uma dependência estranha para o seu script ...


6
Essa parece ser a única solução aqui que usa a compilação do sistema operacional no resolvedor - portanto, funciona para o / etc / hosts e também para o DNS.
Gavin Brock

8
getent, conforme detalhado na outra resposta, também examina o / etc / hosts e vem com o glibc, portanto não possui dependências em um sistema Linux.
Asfand Qazi

44

O seguinte comando usando digpermite ler o resultado diretamente, sem nenhum sed / awk / etc. Magia:

$ dig +short unix.stackexchange.com
64.34.119.12

digtambém está incluído no dnsutilspacote.


Nota : digtem um valor de retorno igual a 0, mesmo que o nome não possa ser resolvido. Portanto, você precisa verificar se a saída está vazia em vez de verificar o 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 : Se um nome de host tiver vários endereços IP (tente debian.org, por exemplo), todos eles serão retornados. Esse "problema" afeta todas as ferramentas mencionadas nesta pergunta até o momento:


2
Observe que, se um domínio tiver uma entrada CNAME, ele poderá ser impresso na primeira linha, em vez de um endereço IP.
Pcworld27 de

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

4
Considere também ahosts, ahostsv4, ahostsv6com getent.
0xC0000022L

cutnão será para getent's, que usam \tpara separar colunas. É o caso do Solaris.
ceving 18/03/16

1
@ ceving: No Solaris, você pode precisar executar cutsem -d(o padrão \té delimitador). No Linux são espaços, portanto a linha acima funciona.
precisa saber é o seguinte

27

As soluções fornecidas até agora funcionam principalmente no caso mais simples: o nome do host é resolvido diretamente para um único endereço IPv4. Este pode ser o único caso em que você precisa resolver nomes de host, mas, se não, a seguir, há uma discussão sobre alguns casos com os quais você pode precisar lidar.

Chris Down e Heinzi discutiram brevemente o caso em que o nome do host resolve para mais de um endereço IP. Nesse caso (e outros abaixo), os scripts básicos sob a suposição de que um nome de host seja resolvido diretamente para um único endereço IP podem ser interrompidos. Abaixo, um exemplo com um nome de host resolvido para mais de um único endereço 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

Mas o que é isso www.l.google.com? É aqui que o caso do alias precisa ser introduzido. Vamos verificar o exemplo abaixo:

$ 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

Portanto www.google.com, não resolve diretamente para endereços IP, mas para um alias que resolve sozinho para vários endereços IP. Para mais informações sobre aliases, clique aqui . Obviamente, o caso em que um alias tem um único endereço IP é possível, como mostrado abaixo:

$ 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

Mas os aliases podem ser encadeados? A resposta é sim:

$ 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

Não encontrei nenhum exemplo em que um nome de host resolva um alias que não seja um endereço IP, mas acho que o caso pode ocorrer.

Mais do que vários endereços IP e apelidos, existem outros casos especiais ... e o IPv6? Você poderia tentar:

$ 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

Onde o nome do host ipv6.google.comé um nome de host somente para IPv6. E os nomes de host de pilha dupla:

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

Novamente sobre o IPv6, se o seu host for apenas IPv4, você ainda poderá resolver os endereços IPv6 (testado em um WinXP somente para IPv4 e com o ipv6.google.com, você poderá experimentá-lo no Linux). Nesse caso, a resolução é bem-sucedida, mas um ping falha com uma mensagem de erro desconhecida do host . Pode ser um caso em que seu script falha.

Espero que essas observações tenham sido úteis.


2
Que excelente complemento para a resposta aceita, mostrando todos os casos extremos com os quais se pode querer lidar com scripts. Minha versão hostnem afirma "tem endereço" para minhas caixas.
Mihai Danila


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

funciona sem dependências em outros sistemas (e para hosts especificados em / etc / hosts)


2
O uso de ping é o que eu precisava, pois preciso do valor do arquivo hosts, mas o padrão sed analisando corretamente, mas isso funcionou como ping -q -c 1 -t 1 your_host_here | grep PING | sed -e "s / ^ [^ (] * [(] //" | sed -e "s ///)).*$//"
ManiacZX

1
Para resolver algo na minha rede doméstica, como myhostname.local, isso funciona; portanto, para mim, esta é a melhor resposta.
Matt Friedman

1
Gostaria de sugerir também: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>também funciona para declarações internas /etc/hosts... e é a ferramenta ideal para todos os tipos de bancos de dados do sistema (passwd, group, aliases, services).
0xC0000022L

17

Simples, mas ú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 os comandos resolverão um endereço IP se o host ainda existir. Se o host apontar para CNAME, ele também obterá o IP nesse caso.

O primeiro comando retorna o endereço IPv4 resolvido

O segundo comando retorna o endereço IPv6 resolvido

O terceiro comando retornará o endereço preferido dos proprietários, o que pode ser endereçado por IPv4 ou IPv6.


De longe o mais simples. E está disponível por padrão. Não é assim hostque exigem a instalação dobindutils
michaelbn

7

Aqui está uma pequena variação da pingabordagem que leva em consideração "host desconhecido" (canalizando pelo stderr) e usa trpara evitar o uso de sedregexps:

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

Caso seja importante capturar o valor de saída, o seguinte funcionará (embora 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}'

Adoro esta solução, porque funciona sem nenhuma ferramenta extra.
Radon8472

7

Para concluir a resposta de Chris Down e abordar os comentários do jfgagne sobre aliases (possivelmente encadeados), aqui está uma solução que:

  • leva em consideração vários IPs
  • leva em conta um ou mais aliases (CNAME)
  • não consulta /etc/hostsarquivo (no meu caso eu não o queria); para consultá-lo, a solução python do dbernt é perfeita)
  • não usa awk / sed

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

Sempre retorna o primeiro endereço IP ou tring vazio, se não for resolvido. com a versão do dig:

    $ dig -v
    DiG 9.8.1-P1

1
Obrigado, outras respostas assumem que "dig + short" sempre retorna um único endereço IP. Eles não estavam respondendo pelos CNAMEs.
jamshid

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

isso funciona, mas requer que o PHP é instalado em seu terminal
Radon8472

1
pode ser útil em um recipiente janela de encaixe típico php, onde "host", "cavar" etc. não estão disponíveis
Fabian Schmengler

5

Gostaria de adicionar isso como um comentário a Andrew McGregor Re: ping. No entanto, não me deixou, por isso preciso adicionar isso como outra resposta. (Se alguém puder colocá-lo em um comentário, sinta-se à vontade.)

Esta é outra variante, apenas usando ping e grep:

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

grep -Epara expressão regular estendida e grep -oretornar apenas a parte correspondente. o próprio regexp procura um ou vários dígitos ( [0-9]+) e, opcionalmente, um ponto ( \.?) quatro vezes ( {4})


4

Você poderia 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

aqui está uma receita do Bash que preparei usando as respostas de outras pessoas - primeiro tente /etc/hostse depois volte ao 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 claro, getent hostsnão é apenas uma pesquisa em / etc / hosts - é uma chamada completa de resolução de DNS para gethostbyaddr (3) , e é muito improvável que falhe em um caso de digsucesso. Veja a página de manual para getent .
Stuart P. Bentley

@ Stuart está certo - eu aprendi muito desde que escrevi isso e simplificou demais um comando poderoso. getentpermanece o meu favorito, embora eu também gostodig +short
RubyTuesdayDONO

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

foi a solução que encontrei sem o servidor DNS


3

Talvez não seja o mais conciso, mas parece ser robusto e 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
}

Isso produzirá um único IPv4 IP, além de retornar 1em caso de falha, mascarando a saída stderr.

Você pode usá-lo assim:

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

O IP do Google é 216.58.192.46.

Se você deseja um endereço IPv6, substitua-o -4por -6.


3

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


1
Algum contexto de como a resposta melhora em relação aos já existentes seria ótimo. Além disso, indente os comandos por 4 espaços (consulte a sintaxe de remarcação).
maxschlepzig

2

1 linha resolve uma lista de nome de host

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

2

Estou fazendo isso o tempo todo no meu Mac, que não possui getent. pingparece um hack. Eu também gostaria de levar /etc/hostsem consideração.

Então, escrevi um invólucro estúpido dns.lookuppara você que possui o Node.js instalado para fornecer uma CLI:

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

60% de chance de quebra antes que você chegue perto de uma resolução.
dotbit 27/08

@dotbit você poderia elaborar? Eu uso isso semanalmente desde 17 e nunca tive nenhum problema.
Thomas Jensen

@ Jensen, mas você é o único de sempre, como sempre. O resto de nós geralmente encontra FAIL de um tipo ou de outro, e sempre.
dotbit

"como sempre" O que você quer dizer com isso? "O resto de nós" Quem é esse? "topar com FAIL" Que problema específico você está vendo? Estou curioso.
Thomas Jensen

2

escavação é muito lenta, nslookup é muito mais rápido

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

2

Não sei a maneira mais fácil para um script bash, mas se você quiser resolver um nome de host e ver se o host está ativo, use ping!

ping -a hostname -c 1

Será pingo host uma vez e resolver o nome do host para o endereço 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 é bom, porque todo mundo tem, mas você precisa filtrar a parte IP das saídas, se quiser usá-la em um script.
Radon8472

1

Sim, já existem muitas respostas, mas falta uma solução usando perl:

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

Em um script bash, poderia ser usado assim:

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

Os módulos usados ​​aqui são módulos principais, portanto devem estar disponíveis em qualquer lugar sem a instalação com o CPAN.


perl -MSocket -MNet::hostent -E 'say inet_ntoa((gethost shift)->addr)' unix.stackexchange.com 2>/dev/null é muito mais limpo. mas ninguém, exceto nós dois, estamos usando pörl, todos os outros usam o Pascal Script, é claro.
Dotbit

Na verdade, prefiro ver as mensagens de erro se algo der errado. Can't call method "addr" on an undefined valuenão é exatamente a melhor mensagem de erro, mas pode dar uma dica sobre o problema.
Slaven Rezic em

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
A versão ruby ​​imprime aspas ao redor do endereço IP --- provavelmente printdeve ser usado em vez de p.
Slaven Rezic em

thx, @Slaven Rezic e sinta-se livre para votar. então, novamente, aqui na parte inferior o script pode ser mais visível, na verdade ... ;-)
dotbit 30/08

-1
host -t a cisco.com

este comando mostrará o endereço IP (reslove o domínio para o IP)


-1

Além da solução acima, você pode traduzir vários nomes de host para ip via script abaixo, a única dependência é o comando "ping" no núcleo 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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.