Les solutions du TP #2

Sommaire

  1. Solution du premier exercice (pilotage de tar par menus)
  2. Solution du deuxième exercice (expressions arithmétiques)
  3. Solution du troisième exercice (modification de l'extension de plusieurs fichiers)
  4. Solution du quatrième exercice (punition de certains utilisateurs)
  5. Solution du cinquième exercice (transcription code source <–> HTML)

1. Solution du premier exercice

Il n'y a rien de bien compliqué, il suffit d'être très rigoureux. Commençons par inclure le code d'une solution parmi l'ensemble des possibilités :

#!/bin/ksh

FIN=0

# Selection de l'operation

echo "Choisissez une operation :"
echo "  Creer une nouvelle archive             1"
echo "  Ajouter des fichiers dans une archive  2"
echo "  Lister le contenu d'une archive        3"
echo "  Extraire le contenu d'une archive      4"
echo "  Quitter le programme                   0"

while [ ${FIN} -eq 0 ]
do
  read NUM_COMMANDE
  echo ${NUM_COMMANDE}
  FIN=1
  case ${NUM_COMMANDE} in
  1) 
    COMMANDE="c";;
  2) 
    COMMANDE="r";;
  3) 
    COMMANDE="t";;
  4) 
    COMMANDE="x";;
  0)
    exit;;
  *) echo "Commande non reconnue => recommencez !"
    FIN=0;;
  esac
done

# Selection des options
  
FIN=0

echo "Choisissez une ou plusieurs options :"
echo "  Mode verbeux                           1"
echo "  Respect des permissions                2"
echo "  Fin                                    0"

while [ ${FIN} -eq 0 ]
do
  echo "Options actuelles : " ${OPTIONS}
  read NUM_OPTION
  FIN=0
  case ${NUM_OPTION} in
  0)
    echo "Fin de selection des options : "${OPTIONS}
    FIN=1;;
  1)
    OPTIONS="${OPTIONS}v";;
  2)
    OPTIONS="${OPTIONS}p";;
  *)
    echo "Option non reconnue";;
  esac
done

# ne pas oublier d'ajouter le f de manipulation des fichiers

OPTIONS=${OPTIONS}f

# Selection du nom de l'archive et tests subsequents

FIN=0
while [ ${FIN} -eq 0 ]
do
  echo "Saisissez le nom de l'archive :"
  read NOM_ARCHIVE
  FIN=1
  case ${NUM_COMMANDE} in
  1)
    if [ -a "${NOM_ARCHIVE}" ]
    then
      echo "Avertissement : le fichier ${NOM_ARCHIVE} existe deja"
      echo "vous pouvez :"
      echo "  L'ecraser                 1"
      echo "  Changer le nom du fichier 2"
      echo "  Passer en mode ajout      3"
      echo "  Abandonner l'operation    0"
      read NUM_COM2
      case ${NUM_COM2} in
   	  0)
	       echo "Abandon des operations"
        exit;;
	     1)
	       rm -f ${NOM_ARCHIVE};;
   	  2)
	       FIN=0;;
   	  3)
	       NUM_COMMANDE=2
      		COMMANDE="r";;
   	  esac
	   fi;;
  2 | 3 | 4)
    if [  -f "${NOM_ARCHIVE}"  ]
    then
      FIN=1
    else
      echo "Erreur : le fichier ${NOM_ARCHIVE} n'existe pas ou n'est pas un fichier normal"
      echo "vous pouvez :"
      echo "  Changer le nom du fichier 1"
      echo "  Abandonner l'operation    0"
      read NUM_COM2
      if [ ${NUM_COM2} -eq 1 ]
      then
        FIN=0
      else
        exit
      fi
    fi;;
   *)
     echo "cas par defaut : bizarre "
     exit;;
  esac
done

# Lecture du nom des fichiers

echo "Choisissez le nom des fichiers"
read SELECTION

# Il ne reste plus qu'a executer la commande idoine !

tar ${COMMANDE}${OPTIONS} ${NOM_ARCHIVE} ${SELECTION}

Quelques commentaires :

2. Solution du deuxième exercice

Cet exercice est très simple, en effet, pour réussir les 2 premières questions, il suffit de travailler avec soin sur une variable compteur que l'on fait évoluer de 1 jusqu'au second argument.

Version avec les expressions arithmétiques de ksh

#!/bin/ksh
if ((  ($# < 2) || ($# > 2)  ))
then
  echo "Nombre d'arguments incorrect "
  echo "Usage : ${0} nom_de_base nombre_de_replications"
else
  NOMBRE_FINAL=${2}
  NOM_BASE=${1}
  COMPTEUR=1
  while (( COMPTEUR <= NOMBRE_FINAL ))
  do
    echo "Ceci est le fichier numero ${COMPTEUR}" > ${NOM_BASE}.${COMPTEUR}
    (( COMPTEUR += 1 ))
  done
fi

Version avec la syntaxe « à la FORTRAN »

#!/bin/ksh
if [  $# -lt 2  ]
then
  echo "Nombre d'arguments incorrect "
  echo "Usage : ${0} nom_de_base nombre_de_replications"
else
  NOMBRE_FINAL=${2}
  NOM_BASE=${1}
  COMPTEUR=1
  while [ ${COMPTEUR} -le ${NOMBRE_FINAL} ]
  do
    echo "Ceci est le fichier numero ${COMPTEUR}" > ${NOM_BASE}.${COMPTEUR}
    COMPTEUR=`expr ${COMPTEUR} + 1`
  done
fi

Version utilisant for

Cette solution est plus amusante. En effet, il nous faut construire par un programme externe la liste des n premiers entiers. Un tel programme n'est pas compliqué à écrire !

#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
  int limite, compteur;

  if (argc != 2)
  {
    printf("Syntaxe non respectee : usage %s n\n",argv[0]);
   	exit(1);
  }

  limite=atoi(argv[1]);

  for (compteur=1;compteur <= limite; compteur ++)
    printf("%d ",compteur);
  puts("");

  return 0;
}

Permettez moi de faire une remarque amusante. Dans les scripts shells, l'argument 0 n'est pas comptabilité dans $# alors qu'il est recensé dans argc pour les programmes C. Un bel exemple d'incohérence, non ?

Maintenant que nous disposons de ce programme (que nous appellerons compteur) il nous suffit de créer une boucle for dont le curseur prendra ses valeurs dans le retour de compteur :

#!/bin/ksh
if ((  $# != 2  ))
then
  echo "Nombre d'arguments incorrect "
  echo "Usage : ${0} nom_de_base nombre_de_replications"
else  
  NOM_BASE=${1}
  PLAGE=`compteur ${2}`
  echo ${PLAGE}

  for COMPTEUR in ${PLAGE}
  do
    echo "Ceci est le fichier numero ${COMPTEUR}" > ${NOM_BASE}.${COMPTEUR}
  done
fi

Et voila le travail ! Amusez vous à comparer les temps d'exécution avec la commande time et donnez m'en des nouvelles !

3. Solution du troisième exercice

Nous allons procéder en plusieurs étapes en nous souvenant que :

La première chose à faire consiste à remplacer les paramètres positionnels par de vraies variables, par exemple :

REP_BASE=$1
EXT_INIT=$2
EXT_FIN=$3

Ceci présente au moins 2 avantages :

Dans un second temps, il faut rechercher la liste des fichiers avec find :

find ${REP_BASE} -name "*.${EXT_INIT}" -print

Il nous faut alors exécuter sed, ce qui ne pose pas de problème spécifique :

CTRL_A=`echo | tr '\012' '\001'`
sed -e "s${CTRL_A}^\(.*\)${EXT_INIT}${CTRL_A}& \1${EXT_FIN}${CTRL_A}"

Examinons néanmoins les lignes de code que nous avons tappées :

  1. La première créée un caractère de séparation « quasi idéal » pour les expressions régulières.
  2. La deuxième applique effectivement sed

Finalement, il ne reste qu'à appliquer la commande mv sur le résultat de sed avec les paramètres suivants :

Le résultat final est le suivant :

#!/bin/ksh

REP_BASE=$1 EXT_INIT=$2 EXT_FIN=$3

CTRL_A=`echo | tr '\012' '\001'`
find ${REP_BASE} -name "*.${EXT_INIT}" -print \
| sed -e "s${CTRL_A}^\(.*\)${EXT_INIT}${CTRL_A}& \1${EXT_FIN}${CTRL_A}" \
| xargs -n2 mv

4. Solution du troisième exercice

Il s'agit d'obtenir la liste de tous les utilisateurs lançant une certaine commande passée en premier argument. La commande ps est utilisée pour obtenir la liste de tous les processus en cours d'exécution, filtrée avec grep pour se limiter à la commande intéressante. Notons que nous n'utilisons pas directement grep sur le nom de la commande mais en prenant bien soin de créer un certain contexte autour du nom de celle-ci. Nous utilisons ensuite awk pour extraire la colonne contenant le nom de l'utilisateur. Afin d'obtenir une seule copie du nom de chaque utilisateur concerné, nous utilisons l'utilitaire standard uniq. Un petit coup d'oeil à man uniq nous apprend que cette commande ne travaille correctement que sur une liste triée, ce qui justifie l'appel intermédiaire à sort.

Le résultat est finalement :

#!/bin/ksh
commande=$1
ps -elf | grep " ${commande}" | awk '{print $1}' | sort | uniq

5. Solution du cinquième exercice

Il s'agit tout simplement d'écrire un script sed contenant de multiples instructions de remplacement. Dans le cas du passage de code source vers HTML, il est impératif de traiter le caractère & en priorité. En effet, dans le cas contraire, comme vos précédents remplacements rajoutent ce caractère, un < serait d'abord remplacé par &lt;, puis par &amplt;!

Dans la transcription inverse, il faut traiter le &amp; en dernier pour éviter qu'une construction HTML, certes rare mais valide, comme &amp;lt; qui devra se traduire comme &lt; (soit, en C, la prise d'adresse de la variable lt) ne se trouve en faite transcrite comme <.

Les deux scripts (eux même traités avec le premier d'entre eux !) sont :

Il faudra, bien entendu, encapsuler ces 2 scripts sed dans des scripts ksh pour les rendre plus agréables à utiliser.