Je suis intéressé si je pourrais utiliser une seule commande grep pour la situation suivante.
J’ai un fichier dhcpd.conf dans lequel les hôtes DHCP sont définis. Étant donné le nom d’hôte, je dois trouver son adresse MAC dans le fichier dhcpd.conf. Je dois l’utiliser pour désactiver sa configuration de démarrage PXE, mais cela ne fait pas partie de cette question.
La syntaxe du fichier est uniforme, mais je veux quand même la rendre un peu infaillible. Voici comment sont définis les hôtes:
host client1 { hardware ethernet 12:23:34:56:78:89; fixed-address 192.168.1.11; filename "pxelinux.0"; } host client2 { hardware ethernet 23:34:45:56:67:78; fixed-address 192.168.1.12; filename "pxelinux.0"; } host client3 { hardware ethernet AB:CD:EF:01:23:45; fixed-address 192.168.1.13; filename "pxelinux.0"; } host client4 { hardware ethernet C1:CA:88:FA:F4:90; fixed-address 192.168.1.14; filename "pxelinux.0"; }
Nous supposons que toutes les configurations ne prennent qu’une seule ligne, même si la syntaxe de dhcpd.conf permet de diviser les options en plusieurs lignes. Nous supposons cependant que l’ordre des options peut différer.
Je suis venu avec la commande grep suivante:
grep -o "^[^#]*host.*${DHCP_HOSTNAME}.*hardware ethernet.*..:..:..:..:..:..;" /etc/dhcp/dhcpd-hosts.conf
Il est censé ignorer les lignes commentées, autoriser les espaces blancs arbitraires entre les jetons et les faire correspondre jusqu’à la fin de l’adresse MAC. Quand je le lance, je reçois des lignes comme ceci:
host client1 { hardware ethernet 12:23:34:56:78:89;
C’est bien! Mais le fait est que je n’ai besoin que d’une adresse MAC, sans la corbeille précédente. Maintenant, je sais que l’utilisation d’un autre grep, ou couper, ou awk pour couper seulement l’adresse MAC de cette sortie serait sortingvial. Mais je me demande, est-il possible d’utiliser une seule commande grep pour obtenir le résultat final, sans avoir à diriger cette sortie vers un autre filtre? Evidemment je ne peux pas oublier le début du pattern, car je veux avoir un nom d’hôte spécifique, correspondant ainsi à ” ..:..:..:..:..:..
” me donnerait tout le MAC adresses.
Encore une fois, je veux une seule commande (pas nécessairement grep) qui supprime uniquement l’adresse MAC correcte du fichier. Je ne suis donc pas intéressé par des solutions qui disent “grep … | grep …” ou “grep … | cut …”, etc.
Bien sûr, dans la pratique, rien de grave ne se produit si j’utilise plusieurs filtres et que je les achemine, je suis juste curieux de savoir s’il est possible de résoudre avec un seul filtre.
J’atsortingbuerais la sortie à une variable.
Vous pouvez utiliser un one-liner Perl pour faire correspondre chaque ligne du fichier avec une seule expression régulière avec un groupe de capture approprié, et pour chaque ligne correspondante, vous pouvez imprimer le sous-fichier.
Il existe plusieurs façons d’utiliser Perl pour cette tâche. Je suggère d’aller avec l’ perl -ne {program}
, qui passe implicitement en boucle sur les lignes de stdin et exécute le programme un-liner une fois pour chaque ligne, la ligne actuelle étant disponible en tant que variable spéciale $_
. (Remarque: l’option -n
n’imprime pas automatiquement la valeur finale de $_
à la fin de chaque itération de la boucle implicite, ce que ferait l’option -p
, c’est-à-dire perl -pe {program}
.)
Voici la solution. Notez que j’ai décidé de passer le nom d’hôte cible en utilisant l’option obscure -s
, ce qui permet d’parsingr les spécifications d’affectation de variables après l’argument {program}
, de la même manière que l’option -v
d’awk. (Il n’est pas possible de passer des arguments de ligne de commande normaux avec l’option -n
car la boucle implicite while (<>) { ... }
englobe tous ces arguments pour les noms de fichiers, mais le mécanisme -s
fournit une excellente solution. Voir Est-il possible de transmettre des arguments de ligne de commande à @ARGV lors de l’utilisation des options -n ou -p?. ) Cette conception évite d’avoir à intégrer la variable $DHCP_HOSTNAME
dans la chaîne {program}
elle-même, ce qui nous permet citez-le et enregistrez quelques barres obliques inverses (en fait 8).
DHCP_HOSTNAME='client3'; perl -nse 'print($1) if m(^\s*host\s*$host\s*\{.*\bhardware\s*ethernet\s*(..:..:..:..:..:..));' -- -host="$DHCP_HOSTNAME"
Je préfère souvent Perl à sed
pour les raisons suivantes:
sed
est plus limité. -M{module}
. sed
n'est pas extensible. sed
raison de son processus en deux passes et de son implémentation hautement optimisée de l'opcode. Voir http://rc3.org/2014/08/28/surprisingly-perl-outperforms-sed-and-awk/ par exemple. sed
, puisque sed
possède un ensemble de commandes plus primitif pour manipuler le texte sous-jacent. Je choisirais sed pour cela, car vous pouvez utiliser une expression rationnelle pour l’adressage de ligne:
sed -e "/host *${DHCP_HOSTNAME}/!d" -e "s/*.\(hardware [^;]*\).*/\1/g"
La première expression supprime toutes les lignes ne correspondant pas à ${DHCP_HOSTNAME}
(vous pourriez vouloir les ${DHCP_HOSTNAME}
dans le shell si vous avez des méta-caractères d’expressions rationnelles dans vos noms d’hôtes, mais je suppose que vous n’en avez pas).
La deuxième expression correspond à la partie d’adresse matérielle et supprime le rest de la ligne.
Vous pouvez essayer Grep -o avec cette expression:
grep -o "[0-9A-F]\{2\}:[0-9A-F]\{2\}:[0-9A-F]\{2\}:[0-9A-F]\{2\}:[0-9A-F]\{2\}:[0-9A-F]\{2\}"
Sortie:
12: 23: 34: 56: 78: 89
23: 34: 45: 56: 67: 78
AB: CD: EF: 01: 23: 45
C1: CA: 88: FA: F4: 90
L’expression ci-dessus ne renverra que l’adresse MAC du fichier de configuration DHCP.
Étant donné que les gens répondent également avec différents outils, je pense que awk
pourrait également être une bonne alternative.
$ cat so host client1 { hardware ethernet 12:23:34:56:78:89; fixed-address 192.168.1.11; filename "pxelinux.0"; } host client2 { hardware ethernet 23:34:45:56:67:78; fixed-address 192.168.1.12; filename "pxelinux.0"; } #host client3 { hardware ethernet AB:CD:EF:01:23:45; fixed-address 192.168.1.13; filename "pxelinux.0"; } host client3 { hardware ethernet AB:CD:EF:01:23:45; fixed-address 192.168.1.13; filename "pxelinux.0"; } host client4 { hardware ethernet C1:CA:88:FA:F4:90; fixed-address 192.168.1.14; filename "pxelinux.0"; } $ awk '/^[^#]/ && /client3/ { printf ("%s: %s\n", $2, $6); }' so client3: AB:CD:EF:01:23:45;
J’utilise une double correspondance pour exclure les lignes commentées et utiliser simplement l’index des champs pour imprimer les informations souhaitées. De cette façon, il devrait également être facile de supprimer la partie PXE. Par exemple, supprimer la directive filename
pour host3 peut se faire comme suit:
$ awk '/^[^#]/ && /client3/ { gsub(/filename[^;]+;/, ""); print; }' so host client3 { hardware ethernet AB:CD:EF:01:23:45; fixed-address 192.168.1.13; }
Spécification d’une image personnalisée (pxecustom.0):
$ awk '/^[^#]/ && /client3/ { gsub(/filename[^;]+;/, "filename \"pxecustom.0\";"); print; }' so host client3 { hardware ethernet AB:CD:EF:01:23:45; fixed-address 192.168.1.13; filename "pxecustom.0"; }