Commande es-tu dans le PATH ?


Objectif :

L’objectif de ce script est de savoir si un programme entré en ligne de commande est présent dans la variable PATH .

Algorithme du script :

Vocabulaire :

La variable PATH , kesako ?

La variable PATH est une variable accessible dans un terminal et contient un ensemble de dossiers dans lesquels seront recherchés les commandes tapées dans le terminal. Chaque commande saisie dans le terminal correspond à un fichier.

Pour voir le contenu de cette variable, il faut ouvrir un terminal ou ne console et taper :

echo $PATH

Vous devriez obtenir ceci en tant qu’utilisateur normal et si la variable PATH n’a pas été modifié :

/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games

Nous avons donc une liste de 5 dossiers séparés par le symbole :

Le script en entier

#!/bin/bash
# Ce script permet de vérifier qu'une commande entrée en ligne 
# de commande se trouve dans la variable PATH


  #Cette fonction essaie de trouver si la commande
  #entrée en ligne de comande se trouve dans le PATH
  #Sortie : 
  # 0 si la commande est trouvée dans le PATH et est exécutable
  # 1 si la commande n'est pas trouvée dans le PATH
dansLePath()
{

   cmd=$1
   cheminPath=$2
   resultat=1
   IFSAncien=$IFS
   IFS=":"

   for dossier in $cheminPath
   do
     if [ -x $dossier/$cmd ] ; then
       resultat=0
     fi
   done

   IFS=$IFSAncien
   return $resultat
 }



 verificationDansPath()
 {
   var=$1

   if [ "$var" != "" ] ; then
     if [ "${var:0:1}" = "/" ] ; then
       if [ ! -x $var ] ; then
         return 1
       fi
     elif !  dansLePath $var "$PATH" ; then
       return 2
     fi 
   fi
}

if [ $# -ne 1 ] ; then
  echo "Usage: $0 command" >&2 ; exit 1
fi

verificationDansPath "$1"
case $? in
  0 ) echo "$1 trouvé dans la variable PATH"  ;;
  1 ) echo "$1 pas trouvé ou pas exécutable"  ;;
  2 ) echo "$1 non trouvé dans la variable PATH"  ;;
esac

exit 0

Explication du script :

La première ligne du script est appelé le “shebang” et permet de préciser au système quel interpréteur utiliser pour interpréter le code.
Cette ligne n’est pas indispensable mais cela fait partie des bonnes pratiques. Dans notre cas nous utiliserons l’interpréteur bash qui se trouve dans /bin pour interpréter le code.

Ensuite vient quelques comentaires, ce sont les lignes qui commencent par le symbole #
Ces lignes ne sont pas interprétées, mais seulement utile pour le développeur ou bien pour assurer une maintenance des scripts plus facile.

Après les commentaires, nous définissons une fonction :

dansLePath()
{
   #....
}

Le nom de la fonction est dansLePath .

Voyons ensemble le détail de cette fonction.

Dans un premier temps, nous déclarons et initialisons 5 variables :

cmd=$1
chemin=$2
resultat=1
oldIFS=$IFS
IFS=":"

Nous pouvons voir $1 et $ 2, cela correspond au paramètres d’entrées qui seront passés à la fonction dansLePath .

Ainsi lorsque nous appelerons la fonction dansLePath cela sera de la manière suivante :

dansLePath “texte1” “texte2”

Nous aurons ainsi la variable cmd contenant la valeur “texte1” et la variable chemin contenant la valeur “texte2”.

Nous déclarons une variable resultat qui est valorisé à 1 et qui correspondra à la variable de sortie de la fonction. Si la commande est trouvée dans la variable PATH , cette variable resultat vaudra 0 , sinon 1 .

Ensuite, nous sauvegardons la variable d’environnement IFS dans la variable oldIFS . Cette variable d’environnement est le séparateur de champ par défaut lorsqu’une liste d’éléments doit être traitée. Les séparateurs de champ par défaut sont la tabulation ( ) et l’espace.
Comme notre variable PATH , contient une liste de dossiers séparés par le symbole : , nous devons modifier le séparateur de champ et ainsi nous modifions la variable d’environnement IFS en la valorisant avec le symbole : .

Pour voir le contenu de la variable d’environnement IFS , il suffit d’ouvrir un terminal en root et de lancer la commande suivante :

Examinons le coeur de la fonction dansLePath .

for dossier in $cheminPath
do
    if [ -x $dossier/$cmd ] ; then
        resultat=0
    fi
done

Nous avons une structure de contrôle qui s’appelle une boucle for .
Avec cette boucle, nous parcourons chaque dossier de la variable PATH et nous stockons chacun des dossiers dans la variable directory .
Suite à cela, nous testons avec la structure de contrôle if si la commande entrée en ligne de commande est exécutable. Si c’est le cas nous valorisons la variable resultat à 0 pour signifier que la commande a été trouvé et est exécutable.

A présent, il reste donc à remettre la valeur originale à la variable d’environnement IFS avec la variable de sauvegarde IFSAncien et également à retourner le résultat de notre fonction, à savoir la variable resultat .

 IFS=$IFSAncien
 return $resultat

La première fonction a été définie et sera appelée dans la seconde fonction expliquée ci-dessous :

     verificationDansPath()
     {
       var=$1

       if [ "$var" != "" ] ; then
         if [ "${var:0:1}" = "/" ] ; then
           if [ ! -x $var ] ; then
             return 1
           fi
         elif !  dansLePath $var "$PATH" ; then
           return 2
         fi 
       fi
    }

Cette fonction s’appelle verificationDansPath et commence par stocker la commande saisie par l’utilisateur dans la variable var à l’aide de la variable particulière $1 .

Nous testons ensuite que la variable var n’est pas vide avec la structure conditionnelle suivante :

if [ “$var” != “” ] ; then
#..
fi

Nous testons ensuite que le premier caractère de la commande est égale à /

if [ "${var:0:1}" = "/" ] ; then
    #....
fi

Si la commande contient comme premier caractère / , cela signifie que la commande est entrée avec un chemin absolu et que cela n’est pas seulement le nom de la commande qui est entrée. Nous pouvons donc tester directement si la commande est exécutable ou non, sans appeler la fonction dansLePath .

if [ ! -x $var ] ; then
#..
fi

Si la commande est exécutable nous retournons 1.

Si la commande est non exécutable ou bien si le premier caractère de la commande n’est pas / , nous testons que la commande saisie par l’utilisateur est contenu dans la variable PATH

 elif !  dansLePath $var "$PATH" ; then

Les deux paramètres d’entrée pour la fonction dansLePath sont
1) la variable var contenant la commande saisie par l’utilisateur
2) la variable PATH contenant la liste des dossiers dans lesquels sont recherchés les programmes

Comme la fonction dansLePath

A présent, que deux fonctions ont été définies. il faut vérifier que l’utilisateur à entrer une commande en entrée du programme et traiter cette commande saisie par l’utilisateur en appelant les fonctions définies plus haut et enfin afficher si la commande a été trouvé dans la variable PATH .

Pour vérifier si l’utilisateur a saisi une commande en entrée du programme, nous allons utiliser une structure conditionnelle avec la variable particulière $# .

if [ $# -ne 1 ] ; then
    echo "Usage: $0 command" >&2 ; exit 1
fi

Cette variable $# contient le nombre de paramètres d’entrée saisis par l’utilisateur lorsque le script shell est lancé.

Si ce nombre est différent de 1, cela signifie que le script a été lancé avec aucun paramètres d’entrée ou bien avec trop de paramètres d’entrée et donc nous affichons un texte sur la sortie standard et nous sortons du programme avec le code de sortie 1, signifiant qu’il y a eu un problème lors de l’exécution du script.

Si tout s’est bien passé, nous n’entrons pas dans le if , il faut donc appeler les deux fonctions définies précédemment. L’ordre d’exécution est le suivant :
1) Nous appelons la fonction verificationDansPath
2) La fonction dansLePath sera appelé dans la fonction verificationDansPath

C’est ce à quoi sert la ligne suivante :

verificationDansPath "$1"

Nous appelons la fonction verificationDansPath avec le paramètre d’entrée $1, qui correspond à ce que l'utilisateur a entré en ligne de commande. Nous plaçons $ 1 entre guillemets car il se peut que l’utilisateur saisisse une phrase au lieu d’une commande. Cela est une bonne pratique.

Suite à cela, nous utilisons la structure case…esac qui est un équivalent de la structure conditionnelle if..elif répétée plusieurs fois.

case $? in
    0 ) echo "$1 trouvé dans la variable PATH"                   ;;
    1 ) echo "$1 pas trouvé ou pas exécutable"     ;;
    2 ) echo "$1 non trouvé dans la variable PATH"               ;;
esac

La syntaxe est la suivante :

    case variable in
        valeur1 ) action1 ;;
        valeur2) action2 ;;
        valeur3) action3 ;;
    esac

Dans notre cas la variable testée est $? . Cette variable est une variable particulière qui correspond au code de sortie de la dernière commande en avant-plan lancée. Ainsi, nous testons si l’instruction suivante retourne 0, 1 ou 2 :

    verificationDansPath "$1"

Si l’instruction retourne 0, nous affichons avec le programme echo que la commande entrée par l’utilisateur a été trouvé dans la variable PATH .
Si l’instruction retourne 1, nous affichons avec le programme echo que la commande entrée par l’utilisateur n’a pas été trouvé dans la variable PATH ou bien que la commande n’est pas exécutable.
Si l’instruction retourne 2, nous affichons avec le programme echo que la commande entrée par l’utilisateur n’a pas été trouvé dans la variable PATH .

La commande suivante permet de quitter le programme en retournant le code de sortie 0. Nous aurions pu mettre n’importe quel chiffre autre que 0, mais 0 signifie généralement que le programme s’est exécuté sans erreur. Cette ligne n’est pas indispensable au script, mais permet de s’assurer que le programme ne continuera pas à s’exécuter une fois fini.

exit 0

Comment exécuter le programme :

Pour exécuter le programme de manière propre, il suffit de se rendre dans un terminal, de se rendre dans le dossier où se trouve le script et lancer la commande suivante qui permet de rendre exécutable le script :

    chmod +x ./dansLePath.sh

Ensuite, nous pouvons exécuter le script avec la commande suivante :

    ./dansLePath.sh

Attention, il ne faut pas oublier ./ dans les deux commandes précédentes.

Voici quelques exemples de sortie du programme :






Améliorer le script :

Dans le script nous avons utilisé une syntaxe qui n’est pas la meilleure pour extraire le premier caractère de la commande saisie par l’utilisateur.

  if [ "${var:0:1}" = "/" ] ; then

La syntaxe est la suivante : $(var:début:longueur}

Cette syntaxe n’est pas présente dans la norme POSIX, norme qui s’efforce d’unifier les commandes shell pour les rendre portables sur tous les sytèmes UNIX-like.

Si nous voulons rendre notre code plus portable, nous pouvons utiliser la syntaxe suivante :

if [ "${var%${var#?}}" = "/" ] ; then
    #...
fi

Explictions ce que signifie cette expression.

${var%motif} enlève la sous-chaîne la plus courte correspond au motif en partant de la fin de chaîne contenue dans la variable var .

Le motif dans notre cas est : ${var#?}

${var#?} enlève la sous-chaîne la plus courte correspond au motif en partant du début de la chaîne contenue dans la variable var.

Le motif ici est ? correspond à un seul caractère (n'importe lequel).

Ainsi si var="/bin/echo", nous aurons ${var#?} = "bin/echo".

Si cette syntaxe est trop complexe, nous pouvons toujours utiliser la commande cut

if [ "$(echo $var | cut -c1)" = "/" ]; then
 #...
fi

Si nous voulons tester que le programme est appelé directement et que le programme n’est pas appelé à partir d’un autre script nous pouvons modifier le code comme suit :

if [ "$BASH_SOURCE" = "$0" ] ; then
        verificationDansPath "$1"
        case $? in
            0 ) echo "$1 trouvé dans la variable PATH"  ;;
            1 ) echo "$1 pas trouvé ou pas exécutable"  ;;
            2 ) echo "$1 non trouvé dans la variable PATH"  ;;
        esac
else
    echo "script non lancé tout seul"
fi

Nous avons rajouter ce test :

if [ "$BASH_SOURCE" = "$0" ] ; then
    #...
else
    echo "script non lancé tout seul"
fi

Ce test nous permet de savoir si la variable d’environnement contient bien le nom du programme qui correspond à la variable $0. Si c’est le cas, cela signifie que le script est lancé tout seul, sinon cela signifie que le script est appelé depuis un autre shell.

Pour tester cela nous pouvons lancer le script seul et voir que le script se déroule sans problème et nous pouvons créer un second script qui appelle notre script et voir que notre script principal ne se lancera pas.

Voici le second script :

#!/bin/bash
#script_dappel.sh
SRC=$(cd $(dirname "$0"); pwd)
source "$SRC/dansLePath.sh"

Voici le script principal :

#!/bin/bash
    # Ce script permet de vérifier qu'une commande entrée en ligne 
    # de commande se trouve dans la variable PATH


      #Cette fonction essaie de trouver si la commande
      #entrée en ligne de comande se trouve dans le PATH
      #Sortie : 
      # 0 si la commande est trouvée dans le PATH et est exécutable
      # 1 si la commande n'est pas trouvée dans le PATH
    dansLePath()
    {

       cmd=$1
       cheminPath=$2
       resultat=1
       IFSAncien=$IFS
       IFS=":"

       for dossier in $cheminPath
       do
         if [ -x $dossier/$cmd ] ; then
           resultat=0
         fi
       done

       IFS=$IFSAncien
       return $resultat
     }



     verificationDansPath()
     {
       var=$1

       if [ "$var" != "" ] ; then
         if [ "${var:0:1}" = "/" ] ; then
           if [ ! -x $var ] ; then
             return 1
           fi
         elif !  dansLePath $var "$PATH" ; then
           return 2
         fi 
       fi
    }

    if [ $# -ne 1 ] ; then
      echo "Usage: $0 command" >&2 ; exit 1
    fi

    if [ "$BASH_SOURCE" = "$0" ] ; then
        verificationDansPath "$1"
        case $? in
            0 ) echo "$1 trouvé dans la variable PATH"  ;;
            1 ) echo "$1 pas trouvé ou pas exécutable"  ;;
            2 ) echo "$1 non trouvé dans la variable PATH"  ;;
        esac
    else
        echo "script non lancé tout seul"
    fi

    exit 0

Le script script_dappel.sh doit être mis dans le même dossier que le script dansLePath.sh . Et n’oubliez pas de rendre exécutable le script script_dappel.sh .


© 2016 - 2017 réalisé par Benjamin LOMBARD