J’ai créé un script qui demande à l’utilisateur d’entrée une adresse IP en vue de la modifié automatiquement dans des fichiers. Les tests sur l’adresse IP fonctionne mais le script est infini.
Voici le code:
#!/bin/sh
#Déclaration variables
TEST_IP=false
#question utilisateur
#IP
while [ "TEST_IP" != "true" ]
do echo "Entrer l'IP du serveur"
read IP
oldIFS=$IFS
IFS=.
set -- $IP
if [ "$#" -ne "4" ]; then
echo "L'IP doit avoir 4 blocs"
else
for oct in $1 $2 $3 $4; do
echo $oct | egrep "^[0-9]+$" >/dev/null 2>&1
if [ "$?" -ne "0" ] || [ "$oct" -lt "0" -o "$oct" -gt "255" ]; then
echo "$oct: IP incorrect"
else
TEST_IP=true
fi
done
fi
done
done
Cela ne doit pas etre grand chose mais je ne suis pas un expert en dev.
Si tu le dis. Dans ce cas il faut entrer “true” à l’invite pour que la boucle s’arrête.
Mais alors je ne vois pas bien à quoi sert la variable TEST_IP.
En général, lorsqu’on a une boucle infinie il faut verifier que :
la condition de sortie de la boucle est possible.
le code contenu dans la boucle peut un jour modifier la condition de sortie
Donc on verifie le premier cas :
Bingo : 2 chaines de caractères ne peuvent pas êtres égales…
En bash, lorsqu’on veut utiliser le contenu d’une variable, il faut utiliser $ ou $(mavar)
Essaye avec “$TEST_IP”
il n’y a pas de quotes autour de true (c’est un mot clef ayant une valeur) :
il y a un done en trop en bas. while…do sont ensembles
Le code fonctionnel :
#!/bin/sh
#Déclaration variables
TEST_IP=false
#question utilisateur
#IP
while [ "$TEST_IP" != true ]
do echo "Entrer l'IP du serveur"
read IP
oldIFS=$IFS
IFS=.
set -- $IP
if [ "$#" -ne "4" ]; then
echo "L'IP doit avoir 4 blocs"
else
for oct in $1 $2 $3 $4; do
echo $oct | egrep "^[0-9]+$" >/dev/null 2>&1
if [ "$?" -ne "0" ] || [ "$oct" -lt "0" -o "$oct" -gt "255" ]; then
echo "$oct: IP incorrect"
else
TEST_IP=true
fi
done
fi
done
Les guillemets autour de “true” sont inutiles, mais dans le contexte de l’expression conditionnelle entre crochets et de l’affectation de variable, “true” est bien une simple chaîne de caractères et non un mot-clé ou une commande.
Bien que le shell comprenne les notions booléennes, une variable shell est true si elle a une valeur affectée, sinon elle est false.
En écrivant TEST_IP=false, tu lui affecte une valeur - de type string, par défaut, si je ne me trompe pas -, elle est donc vraie !
Dans ta boucle while, tu cherches à vérifier que la valeur affectée à la variable TEST_IP, soit bien une chaîne de caractères nommée “true” …
Ce n’est en rien un booléen ! - Là, est souvent l’erreur de raisonnement, en shell, ce n’est pas parce que tu écris une variable=true que cela en fait un booléen True … ce n’est pas l’usage du mot “true” qui en fait un booléen ; variable=false est aussi positionné comme étant vrai !
Dans les deux cas, tu déclares une variable de type string, elles ont de fait pour valeur booléenne, vraie …
Pour qu’une variable soit de valeur booléenne false, il faut simplement la déclarer ainsi : ‘nomvariable’ sans aucune affectation de valeur … dans ce cas, et seulement ce cas, elle est de type booléen false …
Dans ce genre de test, il vaut mieux utiliser des valeurs de type entier, type 0 et 1, car vraiment plus simple à gérer … par le biais de la déclaration ‘typeset -i’ (ou en bash ‘declare -i’) … - autrement cela reviendrait à affecter à ta variable, un caractère de forme 0 ou 1 et non pas l’entier 0 ou 1, ainsi le test correct serait d’utiliser les opérateurs pour chaîne de caractères, et non pas pour entier …
Ton test while deviendrait ainsi - parce qu’on chercherait à tester que la variable TEST_IP soit de type booléen vrai - :
Hors cette écriture est exactement l’équivalent de :
ou sa variante :
Ensuite, ton code est faux, parce qu’en fait, tu sors de ta boucle for, mais jamais de ta boucle while !
Ainsi, ton code corrigé est :
typeset -i TEST_IP=0
oldIFS=$IFS
#question utilisateur
while :
do echo "Entrer l'IP du serveur"
read IP
IFS=.
set -- $IP
if [ "$#" -ne 4 ]; then
echo "L'IP doit avoir 4 blocs"
else
for oct in "$@"; do
echo "$oct" | grep -P "^[0-9]+$" >/dev/null 2>&1
if [ "$?" -ne 0 ] || [ "$oct" -lt 0 -o "$oct" -gt 255 ]; then
echo "$oct: IP incorrect"
TEST_IP=1
fi
done
if [ "$TEST_IP" -eq 0 ]; then
IFS="$oldIFS"
break
else
TEST_IP=0
fi
fi
done
Résultat :
Remarques :
si l’usage de la déclaration ‘typeset -i’ ne fonctionne pas, tu peux écrire ainsi : i[/i] … de même certains shell acceptent l’usage de la commande mathématique ‘let’, tel que : ‘let TEST_IP=0’ … pfff !
la variable oldIFS doit être hors de ta boucle while, sinon tu la réinitialise à chaque fois - pour RIEN -… d’autant que tu ne réinitialises jamais dans ton code IFS … que ce soit quand tu sors de ta boucle for et/ou de ta boucle while !
Un autre point est concernant l’usage du pattern de recherche pour ton grep (ou egrep), utilises l’option -P …
l’usage de ‘set – $IP’ n’est pas conforme, car normalement, il faudrait quoter “$IP” … néanmoins, dans l’immédiat, cela casse le code …
tu remarqueras aussi la modification de la boucle for, par l’usage du paramètre positionnel “$@” : en effet, puisqu’auparavant tu détectes le nombre de paramètres, autant utiliser ce paramètre positionnel plutôt que tes 4 déclarations de paramètres $1 à $4 …
dernier point, je te laisse comprendre l’usage du test conditionnel final sur TEST_IP et surtout l’usage du ‘else’ … test en le supprimant / commentant
Voici le code modifié, pour illustrer le fonctionnement booléen :
set TEST_IP # declaration de la variable sans aucune affection de valeur !
oldIFS=$IFS
#question utilisateur
while :
do echo "Entrer l'IP du serveur"
read IP
IFS=.
set -- $IP
if [ "$#" -ne 4 ]; then
echo "L'IP doit avoir 4 blocs"
else
for oct in "$@"; do
echo "$oct" | grep -P "^[0-9]+$" >/dev/null 2>&1
if [ "$?" -ne 0 ] || [ "$oct" -lt 0 -o "$oct" -gt 255 ]; then
echo "$oct: IP incorrect"
TEST_IP=nimportequoicommevaleur # n'importe quelle valeur est acceptée : '0', 0, tout symbole ... la variable existe, donc elle est vraie
fi
done
if [ "$TEST_IP" ]; then # équivalent de : if test -n "$TEST_IP" ... ou de if [ -n "$TEST_IP" ]
unset TEST_IP
else
IFS="$oldIFS"
break
fi
fi
done