next up previous contents index
suivant: Programme obtenu monter: Recherche des UID et précédent: Méthode utilisée   Table des matières   Index

Développement

Après les commentaires d'entête d'usage, la première chose à faire est d'initialiser les variables d'environnement nécessaires au programme. Toutefois, sachant qu'elles peuvent être définies au niveau du shell, les valeurs spécifiées ici seront les valeurs par défaut. Pour cela, nous utiliserons la syntaxe

"VARIABLE=${VARIABLE:=valeur}"
(cf. section [*]). Nous aurons donc :
TMP_DIR=${TMP_DIR:=/home/adm/tmp}
START_UID=${START_UID:=1000}
START_GID=${START_GID:=1000}
PASSWD=${PASSWD:=/etc/passwd}
GROUP=${GROUP:=/etc/group}
AWK=${AWK:=/usr/ucb/gawk}

export TMP_DIR START_UID START_GID PASSWD GROUP AWK

Remarque 16..1 :


Nous utilisons ici une extension de la commande "awk" : "gawk". Les fonctionnalités et les syntaxes vues à la section [*] sont identiques. Par contre, ses possibilités sont étendues et les options disponibles sur la ligne de commande sont plus importantes. Pour assurer l'indépendance du script, nous utiliserons une variable d'environnement "AWK" référençant l'exécutable "awk" à prendre en compte.

En cas d'erreur de syntaxe, il est de règle de préciser à l'utilisateur le format de la commande, ainsi que les options. Pour cela, nous allons créer une fonction qui affichera la syntaxe à utiliser sur la sortie d'erreur standard, canal à utiliser pour tout affichage de messages d'erreur. Nous aurons donc :

_usage()
{
    echo "Usage: `basename $0` -u|-g" >&2
}

Maintenant, nous allons voir si le nombre d'arguments spécifiés sur la ligne de commandes est conforme. D'après le format explicité au paragraphe [*], ce script doit avoir un et un seul argument sur la ligne de commande qui est l'option spécifiant si la recherche doit s'effectuer pour les UID ou les GID. Par conséquent, la valeur de la variable "#" doit être égal à 1. Dans tous les autres cas, l'appel de cette procédure est incorrect. Nous aurons donc :

if [ $# -lt 1 ]; then
    _usage
    exit 1
fi

Nous devons maintenant :

Nous utiliserons donc l'instruction "case" permettant de faire des branchements conditionnels à partir de la valeur contenue dans le seul et unique argument, présent dans la variable "1" (dont le contenu est obtenu par la séquence "$1"). Nous avons donc :
case $1 in
    -u)
        map=$PASSWD
        start_id=$START_STUDENT_UID
        ;;

    -g)
        map=$GROUP
        start_id=$START_PROJECT_GID
        ;;
    *)
        _usage
        exit 1
        ;;
esac

Sachant que les informations seront extraites à partir du fichier dont le nom est contenu dans la variable locale "map", avant toute opération de traitement de fichier, il est préférable de vérifier que celui-ci existe. Si c'est le cas, aucun problème. Par contre, s'il n'existe pas, on peut considérer que nous sommes en train d'en créer un nouveau de toute pièce. Par conséquent, la première valeur à affecter est la borne inférieure. Nous avons donc :

if [ ! -f $map ]; then
    echo $start_id
    exit 0
fi

À ce point du programme,

Comme nous l'avons vu au paragraphe [*], la méthode utilisée pour calculer cette donnée repose sur :

  1. l'extraction de tous les valeurs déjà affectées,
  2. le tri de ces valeurs,
  3. la sélection des valeurs déjà allouées supérieures ou égales à la borne inférieure,
  4. le calcul de la première valeur disponible.

Pour extraire les données, il suffit d'utiliser la commande "cut" à partir des informations contenues dans le fichier dont le nom est stocké dans la valriable locale "map". Sachant que le champ UID du fichier "passwd(5)" et le champ GID du fichier "group(5)" est, dans les deux cas, le troisième champ et que le séparateur de champ est le caractère ":", il nous suffit d'écrire :

cut -f3 -d: $map
Le résultat est donc envoyé sur la sortie standard, résultat à traiter pour l'étape suivante.

Le tri de ces valeurs doit se faire dans l'ordre numérique. La commande "sort" est don,c utilisée avec l'option "-n". Ici, nous n'avons pas à spécifier de séparateur de champs, ni même de champs sur lesquels le tri est effectué. En effet, chaque enregistrement (c'est-à-dire chaque ligne arrivant sur l'entrée standard), n'est composée que d'un seul champ : le résultat de la commande "cut". Nous avons donc :

cut -f3 -d: $map	|\
    sort -n -u
L'option "-u" de "sort" permet d'éliminer les éventuels doublons16.1Le résultat est donc envoyé sur la sortie standard, résultat à traiter pour l'étape suivante.

L'étape suivante consiste à ne sélectionner les informations dont la valeur est supérieure ou égale à la borne inférieure contenue dans la variable locale "start_id". Nous allons, pour cela, utiliser "awk". À partir de la ligne de commande, on initialise une variable interne à "awk", la variable "min_id", à partir de la valeur contenue dans la variable "start_id" du shell. Si le contenu du premier champ (voire même de la totalité de l'enregistrement) est supérieur ou égal à la variable "min_id", alors l'enregistrement est affiché sur la sortie standard. Nous obtenons donc :

cut -f3 -d: $map                            |\
    sort -n -u                              |\
    $AWK -v min_id=$start_id '$1 >= min_id { print $0}'
Comme pour les étapes précédentes, le résultat obtenu est dirigé sur la sortie standard, canal par défaut pour le filtre "awk".

Remarque 16..2 :


Comme il l'a été précisé précédemment, la variable d'environnement "AWK" permet de référencer l'exécutable "awk" souhaité.

La dernière étape consiste à prendre les données précédentes et de déterminer quelle est la première valeur disponible. Pour cela, nous allons utiliser à nouveau la commande "awk" (ou plus précisemment celle contenue dans la variable d'environnement "AWK"). Comme précédemment, les données arrivant sur l'entrée standard sont une suite de nombres, chacun sur une ligne distincte. Nous allons procéder de la façon suivante :

Nous obtenons donc :
cut -f3 -d: $map                            |\
    sort -n -u                              |\
    $AWK -v min_id=$start_id '$1 >= min_id { print $0}' |\
    $AWK -v min_id=$start_id '
        BEGIN {
            affected_id=min_id
        }
        {
            if ( $1 == affected_id )
                affected_id ++
        }
        END {
            print affected_id
        }'

Sachant que nous voulons récupérer ce résultat dans une variable, il suffit de faire en sorte que l'évaluation de l'expression précédente serve à initialiser une variable. Or nous avons vu que le résultat de l'évaluation était dirigé sur la sortie standard. Grâce à la syntaxe examinée à la section [*], le résultat d'une commande affichée sur la sortie standard peut être mémorisée dans une variable de la façon suivante :

variable=`expression`
Par conséquent, si le résultat de l'expression précédente doit être mémorisée dans la variable shell "no_id", il suffit d'écrire :
no_id=` cut -f3 -d: $map                    |\
    sort -n -u                              |\
    $AWK -v min_id=$start_id '$1 >= min_id { print $0}' |\
    $AWK -v min_id=$start_id '
        BEGIN {
            affected_id=min_id
        }
        {
            if ( $1 == affected_id )
                affected_id ++
        }
        END {
            print affected_id
        }'`

Il ne reste plus qu'à afficher la valeur de la variable sur la sortie standard et terminer avec un code de sortie équivalent à un bon déroulement des opérations. Nous avons donc :

echo $no_id
exit 0


next up previous contents index
suivant: Programme obtenu monter: Recherche des UID et précédent: Méthode utilisée   Table des matières   Index
baudry@esme.fr