Comment supprimer le caractère point de la chaîne sans appeler à nouveau sed ou awk?

12

J'ai un fichier appelé hostlist.txtqui contient du texte comme celui-ci:

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

J'ai le petit script suivant:

#!/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

Quels résultats pour 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

Ma question est de savoir comment supprimer le .juste avant la virgule sans invoquer sedou à gawknouveau? Existe-t-il une étape que je peux effectuer dans les appels existants sedou gawkqui supprimera le point?

hostlist.txt contiendra des milliers d'hôtes donc je veux que mon script soit rapide et efficace.

Linoob
la source
2
Une raison pour laquelle dig +shortça ne marche pas pour vous?
Roger Lipscombe
@RogerLipscombe parce que certains des hôtes de mon hostlist.txt ne sont que des noms d'hôtes, pas des FQDN donc j'utilise + search pour les résoudre.
Linoob

Réponses:

18

La sedcommande, la awkcommande et la suppression de la période de fin peuvent toutes être combinées en une seule commande 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, comme étalé sur plusieurs lignes:

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

Étant donné que la awkcommande suit l' doneinstruction, un seul awkprocessus est appelé. Bien que l'efficacité puisse ne pas avoir d'importance ici, c'est plus efficace que de créer un nouveau processus sed ou awk avec chaque boucle.

Exemple

Avec ce fichier de test:

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

La commande produit:

$ 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

Comment ça fonctionne

awk lit implicitement son entrée un enregistrement (ligne) à la fois. Ce script awk utilise une seule variable, fqui indique si la ligne précédente était un en-tête de section de réponse ou non.

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

    Si la ligne précédente était un en-tête de section de réponse, alors ce fsera vrai et les commandes entre accolades seront exécutées. Le premier supprime la période de fin du premier champ. Le second imprime le premier champ, suivi de ,, suivi du dernier champ. La troisième instruction est remise fà zéro (faux).

    En d'autres termes, fici fonctionne comme une condition logique. Les commandes entre accolades sont exécutées si fest différent de zéro (ce qui, en awk, signifie «vrai»).

  • /ANSWER SECTION/{f=1}

    Si la ligne actuelle contient la chaîne ANSWER SECTION, la variable fest définie sur 1(true).

    Ici, /ANSWER SECTION/sert de condition logique. Il évalue à vrai si le courant correspond à l'expression régulière ANSWER SECTION. Si c'est le cas, la commande entre accolades est exécutée.

John1024
la source
Merci @ John1024! Je ne savais pas que awk n'avait pas besoin d'être dans la boucle (je pensais qu'il n'agirait sur la dernière ligne que s'il était à l'extérieur). Est-ce fune variable arbitraire ou fait f{}-elle explicitement partie des fonctionnalités de awk?
Linoob
Vous êtes le bienvenu. fest une variable arbitraire. Vous pouvez réellement mettre avant les {}conditions logiques complexes. fest juste une condition logique très simple: elle est vraie si non nulle, fausse si nulle.
John1024
@Linoob Notez que dans la deuxième commande, /ANSWER SECTION/joue le rôle de condition logique, analogue au rôle fjoué dans la première commande. J'ai mis à jour la réponse pour en discuter.
John1024
7

digpeut lire un fichier contenant une liste de noms d'hôtes et les traiter un par un. Vous pouvez également demander digde supprimer toutes les sorties à l'exception de la section des réponses.

Cela devrait vous donner le résultat souhaité:

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

awkLa sub()fonction de 'est utilisée pour supprimer la période littérale .de la fin du premier champ. awkImprime ensuite les champs 1 et 5 séparés par une virgule.

REMARQUE: les entrées hostlist.txtqui ne sont pas résolues sont complètement ignorées - elles n'apparaissent pas sur stdout OU stderr.

(Testé sur Linux et FreeBSD)

cas
la source
6

Modifiez votre invocation de gawkcomme suit:

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