CSC 3102 – Introduction aux systèmes d’exploitation

Portail informatique

Devoir Hors Présentiel – Numération en Script shell

cet exercice a pour but de vous familiariser à la manipulation des structures de base du script shell afin que leur utilisation ne soit pas un frein à la réalisation des séances de TP suivantes.

L'exercice consiste à appliquer différentes opérations à une liste d'entiers. Vous allez écrire plusieurs scripts shell pour effectuer ces opérations, ainsi qu'un script principal. Celui-ci permet de sélectionner l'opération, et d'envoyer la liste d'entiers, donnés en paramètres, au script implémentant l'opération. Pour cela, nous procédons pas à pas.

Le devoir doit être rendu via la section dédiée sur la page Moodle du cours. La forme de rendu attendue est l'ensemble des fichiers de script shell que vous allez écrire. Une archive Zip, GZip, Tar, etc. est aussi acceptée.

La charge de travail personnel prévisionnelle pour ce DM est estimée à 3h. Vous êtes incités à le réaliser en autonomie afin de vous familiariser avec les scripts shell. Vous êtes néanmoins autorisés à le réaliser en binôme : dans ce cas, l'un doit rendre le devoir tandis que l'autre doit rendre sous Moodle un fichier indiquant uniquement le nom du binôme avec qui il a travaillé.

testez régulièrement vos avancées en lançant vos scripts avec différentes configurations d'arguments. Vous pouvez même scripter ces différents appels au sein d'un script shell indépendant afin d'automatiser vos tests.

Bon travail !

Readme

Avant d'entamer la partie calculatoire de l'exercice, voici un scénario d'exécution attendu du script num.sh que vous allez implémenter dans les parties suivantes :

$ ./num.sh len 123 4 3 1 $ ./num.sh mir 123 4 56 321 4 65 $ ./num.sh s 123 6 $ ./num.sh Erreur : Argument manquant num.sh applique une opération à chaque nombre d'une liste de nombres entiers positifs donnés en argument. ./num.sh [OP] [ARGS]... OP est l'opération à appliquer, à choisir parmi : * l ou len (pour longueur) : afficher le nombre de chiffres de chaque nombre * m ou mir (pour miroir) : écrire chaque nombre à l'envers (en miroir) * s ou sum (pour somme) : afficher la somme des chiffres de chaque nombre * b ou bin (pour binaire) : écrire chaque nombre, donné sous forme décimale, en binaire * d ou dec (pour décimal) : écrire chaque nombre, donné sous forme binaire, en décimal * i ou int (pour interactif) : commencer la saisie interactive de l'opération à appliquer aux nombres ARGS est la liste des nombres entiers positifs à traiter. $
ce scénario est un bon démarrage de banc de tests, à utiliser systématiquement pour évaluer le bon comportement de votre implémentation. Veillez à le compléter avec vos propres tests !

Remarquez que le dernier lancement du script num.sh ci-dessus aboutit à un message d'erreur, suivi de l'affichage du mode d'emploi de la commande.

Écrivez un script readme.sh qui affiche ce mode d'emploi.

Rendez ce script exécutable, et exécutez-le. Indiquez l'utilisation de l'interpréteur de commandes bash en écrivant à la première ligne de chaque script : #! /bin/bash ; et rendez-le exécutable grâce à la commande chmod u+x <num.sh>.
$ chmod u+x readme.sh $ ./readme.sh

Récupération des arguments et mise en place du squelette calculatoire

L'objet de cette partie consiste à écrire le script num.sh, qui est en charge de récupérer les arguments qui lui sont passés, puis de lancer l'opération demandée sur ceux-ci s'ils sont conformes.

Dans un premier temps, le script vérifie la conformité des arguments :

  • le premier argument doit correspondre à une opération prévue par le mode d'emploi de la commande num.sh (cf. le script readme.sh réalisé à la partie précédente) ;
  • tous les arguments suivants doivent être des entiers positifs.

Dans un second temps, le script exécute l'opération voulue sur ces entiers positifs.

vous avez vu en cours tous les outils vous permettant d'implémenter les opérations. N'utilisez pas des astuces propres au langage Bash que nous ne vous avons pas présentées en cours. Référez-vous plutôt à votre annexe shell.

Ouvrez un nouveau fichier nommé num.sh dans votre éditeur de code.

num.sh applique une opération à une liste d'entiers positifs. Ainsi, un appel correct doit obligatoirement comporter au moins deux arguments : une opération et une opérande.

Testez que le nombre d'arguments passés est correct. En cas d'erreur, affichez un message de sortie d'erreur adéquat, lancez le script readme.sh et terminez num.sh.

Dans une variable op, récupérez la valeur du premier argument. S'il est indiqué dans sa version longue, utilisez un schéma alternatif pour le normaliser sous la forme de sa lettre unique.
Par exemple, l'opération convertissant un entier binaire en décimal peut être invoquée soit en utilisant le mot dec, soit la lettre d. Si dec est utilisé, modifiez la valeur de la variable op pour qu'elle soit égale à d.

l'existence de l'opération sera traitée un peu plus tard.

L'ensemble des arguments suivants sont des entiers positifs auxquels il faudra plus tard appliquer l'opération venant d'être récupérée dans la variable op.
Après avoir décalé d'un cran la liste des paramètres, stockez dans la variable args l'ensemble des arguments restants.

Vérifiez que les nombres dans args sont des entiers.

Pour ce faire, testez sur chacun d'eux l'évaluation d'une opération arithmétique quelconque. En cas d'erreur, affichez un message de sortie d'erreur adéquat, lancez le script readme.sh et terminez num.sh.

Pour lancer l'évaluation d'une opération arithmétique, utilisez la commande expr comme suit : isnum="$(expr 0 + "$arg" 2>/dev/null)", avec arg la variable contenant le nombre à tester. Ensuite, testez si la variable isnum est vide. Si c'est le cas, alors expr a échoué : arg n'est pas un nombre entier.

Si la commande expr échoue, elle ne produit rien sur la sortie standard, mais seulement un message d'erreur sur la sortie d'erreur standard. La commande proposée consiste donc à éliminer la sortie d'erreur standard (en la redirigeant vers /dev/null) pour ne récupérer que la sortie standard. Ensuite, en vérifiant qu'elle n'est pas vide, on s'assure que les opérandes sont des nombres entiers.

Vérifiez que les nombres dans args sont positifs.
Dans le cas contraire, affichez un message de sortie d'erreur adéquat, lancez le script readme.sh et terminez num.sh.

Maintenant que les différents arguments sont corrects et stockés dans des variables indépendantes, il reste à lancer l'opération voulue sur chaque nombre dans args.
Lancer une opération consiste à appeler un script avec un nombre en argument : ce script applique l'opération sur ce nombre. Implémentez l'aiguillage vers l'appel du script effectuant l'opération, à l'aide d'une structure de contrôle permettant un choix alternatif multiple. Les scripts seront implémentés dans la partie suivante, et seront nommés comme suit :

  • len.sh : afficher le nombre de chiffres (la longueur) de l'entier passé en argument ;
  • mirror.sh : écrire à l'envers (en miroir) l'entier passé en argument ;
  • sum_digit.sh : afficher la somme des chiffres de l'entier passé en argument ;
  • decimal_to_binary.sh : écrire en binaire l'entier positif décimal passé en argument ;
  • binary_to_decimal.sh : écrire en décimal l'entier positif binaire passé en argument.
on ne traite pas la saisie interactive pour l'instant.
chaque script ne traite qu'un seul entier positif. Vous devez donc itérer ici sur chaque nombre de la liste.

Si la variable op ne correspond à aucune des opérations décrites dans le script readme.sh, affichez un message de sortie d'erreur adéquat, lancez le script readme.sh et terminez num.sh.

Implémentation des différentes opérations

Dans cette partie, vous devez implémenter chaque opération dans un script séparé. Un script ne traite qu'un seul nombre entier positif.

Comme ces sous-scripts seront principalement invoqués depuis le script principal, ne vous souciez pas de vérifier la conformité de l'argument (sauf pour le script binary_to_decimal.sh de la question 3.e). Pour toutes ces opérations, on ignore d'éventuels 0 inutiles au début des nombres en entrée. Par exemple, ./num.sh len 042 affiche 2 ; et ./num.sh mir 04200 affiche 0024.

pour rappel, vous avez vu en cours tous les outils vous permettant d'implémenter les opérations. N'utilisez pas des astuces propres au langage Bash que nous ne vous avons pas présentées en cours. Référez-vous plutôt à votre annexe shell. Ce faisant, les méthodes d'implémentation des opérations reposent uniquement sur des opérations mathématiques simples : addition, soustraction, multiplication, division entière, modulo.
Vous pouvez utiliser la commande expr pour faire des calculs sur des nombres. Attention à protéger les caractères spéciaux représentant les opérations mathématiques pour expr. Pour plus d'informations : man expr.
pensez à bien tester vos scripts individuellement, au fur et à mesure. Vous devrez les rendre exécutables avec la commande chmod comme d'habitude. Pour être efficace, nous vous conseillons de scripter les tests individuels dans un script appelé test.sh par exemple. N'oubliez pas de rendre tous les scripts exécutables avec la commande chmod.

Implémentez le script len.sh, qui affiche le nombre de chiffres du nombre en argument.
Le nombre de chiffres dans un nombre décimal est lié à la plus grande puissance de 10 inférieure à ce nombre. Tant que le nombre est plus grand que 10, alors on peut enlever des puissances de 10. On peut le diviser par 10 pour comptabiliser et enlever un chiffre, et ainsi de suite.

Implémentez le script mirror.sh, qui écrit à l'envers le nombre en argument. Attention au cas d'un nombre qui se termine par un ou plusieurs 0 : ceux-ci doivent apparaître dans la sortie.
Accumuler en sens inverse chaque chiffre en commençant par les poids faibles dans une variable. Tant qu'il y a un chiffre (celui des unités), l'extraire par une opération modulo, et le concaténer dans le bon ordre dans une variable d'accumulation. Avancer en enlevant ce chiffre (diviser par 10), et ainsi de suite.

Implémentez le script sum_digit.sh, qui affiche la somme des chiffres du nombre en argument.
Cette opération est très proche de l'opération miroir, mais la méthode d'accumulation des chiffres est différente. Tant qu'il y a un chiffre (celui des unités), l'extraire par une opération modulo, et l'additionner à une variable d'accumulation. Avancer en enlevant ce chiffre (diviser par 10), et ainsi de suite.

Implémentez le script decimal_to_binary.sh, qui écrit en binaire le nombre décimal en argument.
Les chiffres dans le résultat de la conversion d'un nombre décimal vers une autre base numérique, sont les restes des divisions successives du nombre à convertir, avec la base numérique de destination. Tant que le nombre à convertir n'est pas nul :
  1. extraire sa conversion vers la base numérique de destination avec une opération modulo ;
  2. avancer en enlevant sa conversion (diviser par la base numérique de destination) ;
  3. concaténer dans le bon ordre le chiffre converti dans une variable d'accumulation.

Implémentez le script binary_to_decimal.sh, qui écrit en décimal le nombre binaire en argument.

Ici, il est possible que l'argument soit invalide : ce peut être un nombre entier positif qui contient des chiffres invalides en binaire, c'est-à-dire différents de 0 et de 1. Prenez ce cas d'erreur en considération : affichez un message de sortie d'erreur adéquat, lancez le script readme.sh et terminez binary_to_decimal.sh.

Dans un nombre d'une base numérique donnée, chaque chiffre indique le multiplicateur à appliquer à la puissance de cette base pour la position du chiffre, et le résultat est la somme de toutes ces puissances.

Par exemple pour le nombre 110 en binaire :

  1. le 0 signifie qu'il y a 0 fois la puissance 0 de la base 2 dans le nombre converti ;
  2. le 1 du centre signifie qu'il y a 1 fois la puissance 1 de la base 2 dans le nombre converti ;
  3. le 1 de gauche signifie qu'il y a 1 fois la puissance 2 de la base 2 dans le nombre converti.
Le résultat est donc 0 x 2^0 + 1 x 2^1 + 1 x 2^2 = 6. Tant qu'il y a un chiffre à convertir :
  1. extraire le chiffre avec une opération modulo ;
  2. avancer en enlevant le chiffre (division) ;
  3. ajouter dans une variable d'accumulation, la puissance de 2 correspondant à la position du chiffre multipliée par ce dernier.

Une version interactive

Dans cette dernière partie, il s'agit de permettre le choix interactif de l'opération de manière à pouvoir en appliquer plusieurs d'affilée sur le même ensemble d'entiers positifs donnés en arguments du script. Pour ce faire, l'opération saisie au lancement du script num.sh sera i ou int.

Voici un exemple d'utilisation du mode interactif :

$ ./num_interactif.sh i 123 4 56 Saisissez une opération parmi l, m, s, b, d ; ou c pour mettre fin au mode interactif. > m 321 4 65 Saisissez une nouvelle opération parmi l, m, s, b, d ; ou c pour mettre fin au mode interactif. > b 1111011 100 111000 Saisissez une nouvelle opération parmi l, m, s, b, d ; ou c pour mettre fin au mode interactif. > l 3 1 2 Saisissez une nouvelle opération parmi l, m, s, b, d ; ou c pour mettre fin au mode interactif. > c

Travaillez dans une copie de num.sh, nommée num_interactif.sh.

Dans num_interactif.sh, si le mode interactif est demandé, positionnez une variable interactive à "true" (ou toute autre valeur pour signifier l'activation du mode interactif). Demandez ensuite la saisie au clavier de l'opération à appliquer (parmi l, m, s, b ou d).

pour simplier la saisie, supposez que seule la lettre de l'opération voulue peut être saisie.

Encapsulez l'aiguillage de l'opération à appliquer (cf. question 2.g) dans une boucle infinie. La dernière étape de cette boucle consiste à demander la saisie d'une nouvelle opération dans le cas où le mode interactif est activé ; sinon il faut mettre fin à la boucle infinie.

ici aussi, et pour simplier la saisie, supposez que seule la lettre de l'opération voulue peut être saisie.

Faites en sorte de terminer num_interactif.sh si le caractère c (pour close) est saisi interactivement par l'utilisateur à la dernière étape de la boucle infinie écrite ci-dessus.