Problème boucle infinie

Bonjour,

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.

Merci

Cordialement

A tout hasard, vérifie le nom de la variable que tu testes dans ton while.

PS : ce genre de question est plutôt pour le forum programmation.

le nom de la variable est bonne.

Pardon d’avoir posté au mauvais endroit

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.

Tu as édité ton message mais tu as mal corrigé, il manque le $.

Pour le reste,

  • il y a un done en trop à la fin ;
  • le test de validité de l’adresse IP est insuffisant : il lui suffit qu’un octet soit bon pour que l’adresse soit considérée comme bonne.

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 … :wink:

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 :wink:

Voili, voilou …

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

:smiley: :wink: