Como você remove o caractere de ponto da string sem chamar sed ou awk novamente?

12

Eu tenho um arquivo chamado hostlist.txtque contém texto como este:

host1.mydomain.com
host2.mydomain.com
anotherhost
www.mydomain.com
login.mydomain.com
somehost
host3.mydomain.com

Eu tenho o seguinte pequeno script:

#!/usr/local/bin/bash

while read host; do
        dig +search @ns1.mydomain.com $host ALL \
        | sed -n '/;; ANSWER SECTION:/{n;p;}';
done <hostlist.txt \
        | gawk '{print $1","$NF}' >fqdn-ip.csv

Qual saída para fqdn-ip.csv:

host1.mydomain.com.,10.0.0.1
host2.mydomain.com.,10.0.0.2
anotherhost.internal.mydomain.com.,10.0.0.11
www.mydomain.com.,10.0.0.10
login.mydomain.com.,10.0.0.12
somehost.internal.mydomain.com.,10.0.0.13
host3.mydomain.com.,10.0.0.3

Minha pergunta é como removo o .antes da vírgula sem chamar sedou gawknovamente? Existe uma etapa que eu possa executar nas chamadas existentes sedou gawkque reduzirão o ponto?

hostlist.txt conterá milhares de hosts, por isso quero que meu script seja rápido e eficiente.

Linoob
fonte
2
Alguma razão pela qual dig +shortnão funciona para você?
22616 Roger Lipscombe
@RogerLipscombe porque alguns dos hosts da minha hostlist.txt são apenas nomes de host, não FQDNs, então estou usando + search para resolvê-los.
Linoob 26/05

Respostas:

18

O sedcomando, oawk comando e a remoção do período final podem ser combinados em um único comando awk:

while read -r host; do dig +search "$host" ALL; done <hostlist.txt | awk 'f{sub(/.$/,"",$1); print $1", "$NF; f=0} /ANSWER SECTION/{f=1}'

Ou, como distribuídos por várias linhas:

while read -r host
do
    dig +search "$host" ALL
done <hostlist.txt | awk 'f{sub(/.$/,"",$1); print $1", "$NF; f=0} /ANSWER SECTION/{f=1}'

Porque o awk comando segue a doneinstrução, apenas um awkprocesso é chamado. Embora a eficiência possa não ter importância aqui, isso é mais eficiente do que criar um novo processo sed ou awk a cada loop.

Exemplo

Com este arquivo de teste:

$ cat hostlist.txt 
www.google.com
fd-fp3.wg1.b.yahoo.com

O comando produz:

$ while read -r host; do dig +search "$host" ALL; done <hostlist.txt | awk 'f{sub(/.$/,"",$1); print $1", "$NF; f=0} /ANSWER SECTION/{f=1}'
www.google.com, 216.58.193.196
fd-fp3.wg1.b.yahoo.com, 206.190.36.45

Como funciona

O awk lê implicitamente sua entrada, um registro (linha) de cada vez. Esse script do awk usa uma única variável, fque indica se a linha anterior era ou não um cabeçalho da seção de resposta.

  • f{sub(/.$/,"",$1); print $1", "$NF; f=0}

    Se a linha anterior era um cabeçalho da seção de resposta, fserá verdadeira e os comandos entre chaves são executados. O primeiro remove o período à direita do primeiro campo. O segundo imprime o primeiro campo, seguido por ,, seguido pelo último campo. A terceira instrução é redefinida fpara zero (false).

    Em outras palavras, faqui funciona como uma condição lógica. Os comandos entre chaves são executados se ffor diferente de zero (o que, no awk, significa 'true').

  • /ANSWER SECTION/{f=1}

    Se a linha atual contiver a sequência ANSWER SECTION, a variável fserá configurada como 1(true).

    Aqui, /ANSWER SECTION/serve como uma condição lógica. É avaliado como verdadeiro se a corrente corresponder à expressão regular ANSWER SECTION. Se isso acontecer, o comando entre chaves será executado.

John1024
fonte
Obrigado @ John1024! Eu não sabia que o awk não precisava estar dentro do loop (eu pensei que só atuaria na última linha se estivesse fora). É fuma variável arbitrária ou é f{}uma parte explícita da funcionalidade do awk?
Linoob 26/05
Não há de quê. fé uma variável arbitrária. Você pode realmente colocar antes das {}complexas condições lógicas. fé apenas uma condição lógica muito simples: é verdadeira se diferente de zero, falsa se zero.
John1024
@Linoob Observe que no segundo comando, /ANSWER SECTION/desempenha o papel de condição lógica, análogo ao papel fdesempenhado no primeiro comando. Atualizei a resposta para discutir isso.
John1024
7

digpode ler em um arquivo que contém uma lista de nomes de host e processá-los um por um. Você também pode dizer digpara suprimir toda a saída, exceto a seção de resposta.

Isso deve fornecer a saída desejada:

dig -f hostlist.txt +noall +answer +search | 
    awk '{sub(/\.$/,"",$1); print $1","$5}'

awkA sub()função de é usada para retirar o período literal .do final do primeiro campo. Entãoawk imprime os campos 1 e 5 separados por vírgula.

NOTA: entradas em hostlist.txt que não são resolvidas são completamente descartadas - elas não aparecem no stdout OU no stderr.

(Testado no Linux e FreeBSD)

cas
fonte
6

Altere sua chamada de gawkpara o seguinte:

| gawk '{print substr($1,1,length($1)-1)","$NF}' >fqdn-ip.csv
DopeGhoti
fonte