Nous pouvons utiliser la syntaxe ${var##pattern}
et ${var%%pattern}
pour extraire la dernière et la première section d'une adresse IPv4:
IP=109.96.77.15
echo IP: $IP
echo 'Extract the first section using ${var%%pattern}: ' ${IP%%.*}
echo 'Extract the last section using ${var##pattern}: ' ${IP##*.}
Comment extraire la deuxième ou la troisième section d'une adresse IPv4 en utilisant l'expansion des paramètres?
Voici ma solution: j'utilise un tableau et change la variable IFS.
:~/bin$ IP=109.96.77.15
:~/bin$ IFS=. read -a ArrIP<<<"$IP"
:~/bin$ echo ${ArrIP[1]}
96
:~/bin$ printf "%s\n" "${ArrIP[@]}"
109
96
77
15
Aussi j'ai écrit des solutions en utilisant les awk
, sed
et les cut
commandes.
Maintenant, ma question est: existe-t-il une solution plus simple basée sur l' expansion des paramètres qui n'utilise pas de changement de tableau et IFS?
IFS
pour leread
là:IFS=. read -a ArrIP<<<"$IP"
IFS=. read a b c d <<< "$IP"
n'est pas acceptable (si vous utilisez Bash, qui est)? Pourquoi faut-il le faire avec l'expansion des paramètres?Réponses:
En supposant que la valeur par défaut de IFS vous extrayez chaque octet dans sa propre variable avec:
Ou dans un tableau avec:
la source
Votre énoncé de problème peut être un peu plus libéral que vous ne le vouliez. Au risque d'exploiter une faille, voici la solution à laquelle Muru a fait allusion :
C'est quelque peu maladroit. Il définit deux variables à jeter et n'est pas facilement adapté pour gérer plus de sections (par exemple, pour une adresse MAC ou IPv6). La réponse de Sergiy Kolodyazhnyy m'a inspiré à généraliser ce qui précède à ceci:
Ceci définit
sec1
,sec2
,sec3
etsec4
qui peuvent être vérifiéeswhile
boucle doit être facile à comprendre - elle se répète quatre fois.slice
le nom d'une variable qui remplacelast3
etlast2
dans ma première solution (ci-dessus).declare sec"$count"="value"
est un moyen d'assigner àsec1
,sec2
,sec3
etsec4
quandcount
est1
,2
,3
et4
. C'est un peu commeeval
, mais plus sûr.value
,"${slice%%.*}"
est analogue à des valeurs mes ayants réponse originalesfirst
,second
etthird
.la source
Je me rends compte que vous avez spécifiquement demandé une solution qui N'A PAS REDÉFINI temporairement
IFS
, mais j'ai une solution douce et simple que vous n'avez pas couverte, alors voici:Cette commande courte mettra les éléments de votre adresse IP dans le shell paramètres de position
$1
,$2
,$3
,$4
. Cependant, vous voudrez probablement d'abord enregistrer l'originalIFS
et le restaurer ensuite.Qui sait? Vous allez peut-être reconsidérer et accepter cette réponse pour sa brièveté et son efficacité.
(Cela était auparavant incorrectement donné comme
IFS=. set -- $IP
)la source
IFS
sur la même ligne de commande, la nouvelle valeur ne prend pas effet lorsque les variables sur la même ligne de commande sont développées. Comme avecx=1; x=2 echo $x
set
n'utilise pas du$IFS
tout,$IFS
n'est utilisé que pour le fractionnement du mot$IP
, mais ici, il est attribué trop tard, donc cela n'a aucun effet. Cette réponse est fondamentalement fausse.IP=109.96.77.15 bash -c 'IFS=. set -- $IP; echo "$2"'
ne produit rien que ce soit en mode POSIX ou non. Vous auriez besoinIFS=. command eval 'set -- $IP'
, ouIFS=. read a b c d << "$IP"
.
dans l'un de vos tests précédents. CourezIP=1.2.3.4 bash -xc 'IFS=. set $IP; echo "$2"'
et vous verrez que cela ne fonctionne pas. Et voyezIP=1.2.3.4 bash -o posix -xc 'IFS=. set $IP; echo "\$1=$1 \$2=$2 IFS=$IFS"'
pour illustrer le point de @ chepner.Pas le plus simple , mais vous pourriez faire quelque chose comme:
Cela devrait fonctionner dans ksh93 (où que l'
${var//pattern/replacement}
opérateur vient),bash
4.3 +, busyboxsh
,yash
,mksh
etzsh
, même si bien sûr danszsh
, il y a des approches beaucoup plus simples . Dans les anciennes versions debash
, vous devez supprimer les guillemets internes. Cela fonctionne avec ces guillemets internes supprimés dans la plupart des autres shells, mais pas ksh93.Cela suppose qu'il
$IP
contient une représentation quadri-décimale valide d'une adresse IPv4 (bien que cela fonctionnerait également pour des représentations quad-hexadécimales comme0x6d.0x60.0x4d.0xf
(et même octal dans certains shells) mais produirait les valeurs en décimal). Si le contenu de$IP
provient d'une source non fiable, cela équivaudrait à une vulnérabilité d'injection de commande.Au fond, comme nous remplaçons tous
.
dans$IP
avec+256*(
, on finit par évaluer:Nous sommes donc construire un entier de 32 bits sur ces 4 octets comme une adresse IPv4 est en fin de compte (mais avec les octets inversés) ¹, puis en utilisant les
>>
,&
opérateurs de bits pour extraire les octets pertinents.Nous utilisons l'
${param+value}
opérateur standard (ici sur$-
lequel il est garanti d'être toujours défini) au lieu de simplementvalue
parce que sinon l'analyseur arithmétique se plaindrait de parenthèses incompatibles. Le shell ici peut trouver la fermeture))
de l'ouverture$((
, puis effectuer les extensions à l'intérieur qui entraîneront l'expression arithmétique à évaluer.Avec à la
place, le shell traiterait le deuxième et le troisième$(((${IP//./"+256*("}))))&255))
)
s comme la fermeture))
de$((
et rapporterait une erreur de syntaxe.Dans ksh93, vous pouvez également faire:
bash
,mksh
,zsh
Ont copié de ksh93${var/pattern/replacement}
opérateur , mais pas cette partie de manutention capture groupe.zsh
le prend en charge avec une syntaxe différente:bash
prend en charge une certaine forme de gestion de groupe de capture dans son opérateur de correspondance d'expressions régulières , mais pas dans${var/pattern/replacement}
.POSIXly, vous utiliseriez:
Le
noglob
pour éviter les mauvaises surprises pour les valeurs de$IP
like10.*.*.*
, le sous-shell pour limiter la portée de ces changements d'options et$IFS
.¹ Une adresse IPv4 n'est qu'un entier de 32 bits et 127.0.0.1, par exemple, n'est qu'une des nombreuses représentations textuelles (bien que les plus courantes). Cette même adresse IPv4 typique de l'interface de bouclage peut également être représentée comme 0x7f000001 ou 127.1 (peut-être une plus appropriée ici pour dire que c'est l'
1
adresse sur le réseau 127.0 / 8 classe A), ou 0177.0.1, ou les autres combinaisons de 1 à 4 nombres exprimés en octal, décimal ou hexadécimal. Vous pouvez passer tous ceux-ci à,ping
par exemple, et vous verrez qu'ils cingleront tous localhost.Si cela ne vous dérange pas l'effet secondaire de la définition d'une variable temporaire arbitraire (ici
$n
), dansbash
ouksh93
ouzsh -o octalzeroes
oulksh -o posix
, vous pouvez simplement reconvertir toutes ces représentations en un entier 32 bits avec:Et puis extrayez tous les composants avec
>>
/&
combinaisons comme ci-dessus.mksh
utilise des entiers 32 bits signés pour ses expressions arithmétiques, vous pouvez l'utiliser$((# n=32,...))
pour forcer l'utilisation de nombres 32 bits non signés (et l'posix
option pour qu'il reconnaisse les constantes octales).la source
${-+
auparavant. Je ne trouve pas non plus de documentation dessus. Cela fonctionne, mais je suis juste curieux de confirmer, est-ce juste pour transformer la chaîne en une expression mathématique? Où puis-je trouver la définition officielle? De plus, les citations supplémentaires dans la section de remplacement de l'extension des paramètres ne fonctionnent pas dans GNU bash, version 4.1.2 (2) -release CentOS 6.6. Je devais le faire à la placeecho "$((${-+"(${IP//./+256*(}))))"}>>16&255))"
Avec zsh, vous pouvez imbriquer des substitutions de paramètres:
Ce n'est pas possible dans bash.
la source
zsh
, vous pouvez préférer${${(s(.))ip}[3]}
Bien sûr, jouons au jeu de l'éléphant.
ou
la source
Avec
IP=12.34.56.78
.Et
La description:
Utilisation de l'expansion des paramètres
${IP// }
pour convertir chaque point de l'ip en une parenthèse ouvrante, un point et une parenthèse fermante. En ajoutant une parenthèse initiale et une parenthèse fermante, nous obtenons:ce qui créera quatre parenthèses de capture pour la correspondance d'expression régulière dans la syntaxe de test:
Cela permet l'impression du tableau BASH_REMATCH sans le premier composant (la correspondance regex entière):
La quantité de parenthèses est automatiquement ajustée à la chaîne correspondante. Ainsi, cela correspondra soit à un MAC soit à un EUI-64 d'une adresse IPv6 malgré leur longueur différente:
En l'utilisant:
la source
Voici une petite solution faite avec POSIX
/bin/sh
(dans mon cas, c'est le casdash
), une fonction qui utilise à plusieurs reprises l'expansion des paramètres (donc pasIFS
ici), et des canaux nommés, et inclut unenoglob
option pour les raisons mentionnées dans la réponse de Stéphane .Cela fonctionne comme suit:
Et avec
ip
changé en109.*.*.*
Le compteur de maintien de boucle de 4 itérations représente 4 sections d'une adresse IPv4 valide, tandis que les acrobaties avec des canaux nommés expliquent la nécessité d'utiliser davantage des sections d'adresse IP dans le script, au lieu d'avoir des variables coincées dans un sous-shell d'une boucle.
la source
Pourquoi ne pas utiliser une solution simple avec awk?
$ IP="192.168.1.1" $ echo $IP | awk -F '.' '{ print $1" "$2" "$3" "$4;}'
Résultat
$ 192 168 1 1
la source
la source