MO101 - Introduction à Linux - Feuille TD3

Bienvenue dans le troisième TD du cours MO101 !

Au cours de ce TD, nous allons appronfondir nos connaissances de la ligne de commande, notamment de la fonction d'historique de bash. Ensuite, nous verrons quelques fonctions de gestion avancée de l'arborescence, en particulier la notion d'archivage, qui sera mise en application pour la remise de vos travaux pendant le contrôle. Finalement, nous ferons un court survol de quelques notions de processus sous UNIX.

Partie 1 - Un peu plus de Bash

Une fonction bien pratique de la ligne de command bash est qu'elle conserve l'historique des dernières commandes entrées. Vous avez peut-être déjà utilisé les flèches haut et bas pour parcourir cet historique pendant le TD précédent. Voici donc quelques notions de base pour utiliser cet historique :

Tentons maintenant d'utiliser ces outils.

Q1. Affichez toutes les commandes "cd" que vous avez utilisées. Pensez à utiliser les outils vus dans le précédent TD.


Réponse :

    history | grep cd | less ("| less" est facultatif)

Q2. Avec Ctrl-R, retrouvez et exécutez la dernière commande "ls" que vous avez effectuée.


Réponse : Ctrl-R, puis "ls", puis _Entrée_.

Partie 2 - Gestion avancée de l'arborescence et des fichiers

Cette partie du TD se divise en trois sous-sections : l'archivage, des outils pratiques pour le suivi de votre compte UNIX à l'ENSTA, et la notion de droits d'accès. Commençons donc par l'archivage, avec la présentation de la commande tar, en deux versions :

tar -czvf archive.tar.gz fichiers
tar -xzvf archive.tar.gz

La première version permet de créer une archive appelée "archive.tar.gz" à partir de la liste de fichiers donnée par la suite des arguments, et la deuxième d'en extraire le contenu dans le dossier en cours. Pour donner la liste de fichiers à archiver, on peut évidemment utiliser les outils de sélection montrés précédement dans ce cours (par exemple, "*.csv" pour tous les fichiers ayant pour extension ".csv"). Si on donne un ou des noms de dossiers, leur contenu est inclus.

On remarque ici une façon compacte de donner des options à une commande : si chaque argument n'a qu'une lettre, ils peuvent être combinés en un seul bloc précédé d'un tiret. Ainsi, "-czvf archive.tar.gz" correspond à "-c -z -v -f archive.tar.gz" Voici la signification de chacun des arguments :

Vous voyez donc que l'outil tar permet à la fois d'archiver, compresser, décompresser et désarchiver des archives. En fait, tar fait plutôt appel à un autre outil, gzip, pour la compression. En effet, la commande "tar -czvf archive.tar.gz fichiers" correspond à "tar -cv fichiers | gzip > archive.tar.gz". C'est aussi pourquoi les archives ont l'extension ".tar.gz" : c'est une archive ".tar", compressée en format ".gz". L'idée est que gzip compresse un flux de données, et pas nécessairement une arborescence de fichiers. C'est le rôle de tar de conserver la structure de l'arborescence dans une archive. C'est une manifestation d'un concept important d'UNIX : on combine plusieurs petits utilitaires, tar et gzip, plutôt que d'en créer un seul immense. Cela permet également d'offrir d'autres format de compression, notamment "bzip", en remplaçant simplement gzip dans la commande (option "-j" de tar).

Il est maintenant le temps de se servir de cet outil. Commencez par récupérer l'archive pour ce TD, mais ne la désarchivez pas tout de suite :

archive_td3.tar.gz

Une fois téléchargée, l'archive devrait se trouver dans le dossier "Téléchargements" de votre dossier personnel.

Q3.a) À l'aide de la ligne de commande, commencez par transférer cette archive dans votre dossier mo101.


Réponse (si vous venez d'ouvrir un terminal et que vous êtes dans votre dossier personnel) :

    mv Téléchargements/archive_td3.tar.gz mo101/

Si vous êtes déjà dans le dossier Téléchargements :

    mv archive_td3.tar.gz ../mo101/

Ou de n'importe où :

    mv ~/Téléchargements/archive_td3.tar.gz ~/mo101/

Q3.b) À partir de votre dossier mo101, désarchivez l'archive du TD3 à la ligne de commande.


Réponse :

    tar -xzvf archive_td3.tar.gz

Vous devriez maintenant avoir son contenu dans un dossier "td3". Rendez-vous dans ce sous-dossier, et répondez à la prochaine question.

Q4. Archivez tous les fichiers "*.txt" dans une archive nommée "texte.tar.gz"


Réponse :

    tar -czvf texte.tar.gz *.txt

Tout au long de ce cours, nous avons manipulé des fichiers qui occupent très peu d'espace. Or, vous vous doutez sûrement que l'espace dont vous disposez sur les serveurs de l'ENSTA est limité. Pour connaître à la fois quel espace est utilisé et de combien on dispose, voici quelques outils pratiques :

Dans le cas des commandes du et df, l'option "-h" (pour "human-readable") est conseillée, puisque l'affichage des tailles sera en unités de Ko, Mo et Go plutôt qu'en nombre de blocs de 1024 octets.

Q5. Affichez l'espace disque utilisé par le dossier "td3".


Réponse (à partir du dossier personnel) :

    du -h td3

Vous avez appris en amphithéâtre que l'accès aux fichiers est géré par une série de droits d'accès. En voici un cours rappel :

Nous allons voir deux commandes qui nous permettent de manipuler ces droits :

Si ce n'est pas déjà fait, rendez-vous dans le dossier "td3" pour répondre aux prochaines questions.

Q7. Affichez les droits d'accès des fichiers dans "td3"


Réponse :

    ls -l

Q8. Ajoutez le droit d'exécution au propriétaire du fichier "bonjour.sh".


Réponse :

    chmod u+x bonjour.sh

Partie 3 - Manipulation des processus

Vous avez vu en amphithéâtre que chaque commande sous UNIX est un processus. En effet, la ligne de commande est un processus en soi qui lance de nouveaux processus pour chacune des commandes que vous lui donnez. Nous allons maintenant voir comment manipuler ces processus avec ces quelques combinaisons de touche et commandes :

Dans la partie précédente, vous avez donné des droits en exécution au fichier "bonjour.sh". Vous pouvez maintenant l'exécuter à partir du dossier td3 avec :

./bonjour.sh

Or, vous remarquerez qu'après vous avoir dit "bonjour", vous ne récupérez pas le contrôle de la ligne de commande. Explorons donc les outils présentés ci-dessus avec ces quelques questions :

Q9.a) Mettez le processus en pause et en arrière-plan.


Réponse :

    Ctrl-Z
    bg

Q9.b) Remettez le processus en premier-plan.


Réponse :

    fg

Q9.c) Interrompez le processus.


Réponse :

    Ctrl-C

Maintenant, vous pouvez redémarrer le processus et répondre à la dernière question :

Q10.a) Mettez le processus en pause.


Réponse :

   Ctrl-Z

Q10.b) Trouvez le PID de ce processus en arrière-plan.


Réponse :

    ps

ou, pour plus de détails

    ps -f


Partie 4 - Scripts Shell

Quand on veut faire des opérations complexes qui utilisent une combinaison importante de commande Linux, il est possible de mettre ces commandes dans un fichier, c'est-à-dire "un script shell", qui va automatiser l'exécution de ces commandes.

De plus, le shell bash (et les autres shell aussi) possède des constructions similaires aux langages de programmation comme Python, C++. Il y a, en particulier, des structures conditionnelles, des boucles. L'objectif de cette partie est de vous présenter les principales constructions des scripts shell.

Q17.a) Un premier script

Afficher le contenu du script "arguments.sh"


Réponse :

    cat arguments.sh

ou, pour naviguer facilement dans le fichier

    less arguments.sh


Quelques commentaires sur ce premier script:
- Par convention la première ligne d'un script bash commence par "#! " suivi du chemin vers
l'interprète de commandes, i.e., le shell.
- Il est possible de manipuler des variables dans les scripts shell (également dans le terminal !!).
On donne une valeur à la variable "foo" en utilisant la syntaxe suivante "foo=expression".
Le contenu de la variable "foo" est obetnu en ajoutant un symbole "$" devant le nom de la variable,
c'est-à-dire, en écrivant "$foo".
- Un script shell est capable de gérer des paramètres sur la ligne de commande et il existe
différentes variables associées à ces paramètres.


Q17.b) Exécutez le script "arguments.sh" et observez le résultat


Réponse :

Sans paramètres

    bash arguments.sh

ou, avec paramètres

    bash arguments.sh a 1 b 2 c

Note: En ajoutant les droits d'exécution au script "arguments.sh" (avec la commande "chmod u+x arguments.sh"),
l'exécution du script se réalise en tapant la commande "./arguments.sh".

Q17.c) Etudiez et exécutez le script "expansion-variables.sh", que remarquez-vous ?


Réponse :

En bash, une chaine de caractères peut être délimitée par des apostrophes ' ou des doubles guillemets ".
Une variable utilisée dans une chaine délimitée par des doubles guillemets sera **expansée**, c'est-à-dire
que le contenu de la variable sera affiché. A l'inverse les chaines des caractères entre apostrophes n'autorisent pas cette expansion.


Q18) Mon premier script

En vous inspirant du script "arguments.sh", écrivez un script shell nommé "arborescence.sh" qui crée, dans le répertoire "td3", l'arborescence de répertoires de la forme suivante.

td3
|-- bin/
|-- lib/
|-- var/
    |-- log/
    |-- msgs/

Réponse :

    #! /bin/bash

    mkdir bin lib var
    cd var
    mkdir log msgs

ou en une seule ligne

   #! /bin/bash
   mkdir -p bin lib var/log var/msgs


Q19.a) La structure conditionnelle en shell

Une sctucture conditionnelle simple en Shell a la syntaxe suivante:

if commande
then
  <faire quelque chose>
fi

"commande" doit renvoyer un code de retour pour savoir si elle a réussi ou si elle a échoué. Souvent on utilise la commande "test" qui permet de faire des comparaisons ou tester le type de fichier. Un raccourci existe pour la commande "test" qui est la notation crochet "[ ]" mais qui demande que le crochet ouvrant "[" soit suivi d'un espace et que le crochet fermant "]" soit précéder d'un espace donc de la forme "[ expression ]" !!

Par exemple, si on veut savoir si le premier paramètre d'un script est plus grand que 10 on peut écrire

#! /bin/bash

if [ $1 -gt 10 ]
then
   echo "Plus grand que 10"
fi

Un court extrait de la page "man" de la commande "test" vous donne les opérateurs de comparaisons importants pour ces exercices

Ecrivez un script shell, nommé "est_repertoire.sh", qui prend en paramètre un nom de fichier et affiche si le fichier est en réalité un dossier.


Réponse :

    #! /bin/bash

    if [ -d $1 ]
    then
      echo $1 : est un dossier
    fi


Q19.b) Compétez le script précédent pour tester si le paramètre est un dossier seulement si le nombre d'arguments du script est strictement positif.


Réponse :

    #! /bin/bash

    if [ $# -gt 0 ]
    then
       if [ -d $1 ]
       then
          echo $1 : est un dossier
       fi
    fi


Q20.a) La boucle en shell

Une boucle "for" en bash a la syntaxe suivante

for ident in liste_valeurs
do
   instructions
done

"ident" est un nom de variable. "instructions" représente un ensemble de commandes. Cette boucle applique un traitement sur chaque élément de la liste "liste_valeurs", c'est le même genre de boucle "for" que celle de Python.

Par exemple, on peut afficher cinq fois bonjour avec le script suivant :

#! /bin/bash

for INDEX in 1 2 3 4 5
do
   echo "Bonjour"
done

En vous inspirant de cet exemple, écrire un script "creer_fichiers.sh" qui crée 5 fichiers vides avec un nom de la forme "fichier_1.txt", "fichier_2.txt", ..., "fichier_5.txt"


Réponse :

    #! /bin/bash

    for INDEX in 1 2 3 4 5
    do
        touch fichier_$INDEX.txt
    done


Q20.b) La boucle en shell, suite

Il est possible d'utiliser le résultat d'une commande pour créer la liste de valeurs "liste_valeurs" en paramètre de la boucle "for", par exemple, si on veut faire un traitement sur des fichiers ou répertoires. Cependant, il est nécessaire d'utiliser une syntaxe particulière pour récupérer le résultat d'une commande, celle-ci est la notation "$()".

Par exemple, pour faire un script qui a le même rôle que la commande "ls" on pourrait écrire

#! /bin/bash

for FICHIER in $(ls)
do
  echo $FICHIER
done

La commande "seq" (cf "man seq") permet de générer des suites de valeurs entières, par exemple, "seq 1 10" génère la liste des dix premiers entiers strictement positifs. Plus précisément, la commande "seq" prend en paramètre le nombre qui débute la séquence et le nombre qui termine la séquence.

Reprenez le scripts de la question Q15.a) pour générer 100 fichiers.


Réponse :

    #! /bin/bash

    for i in $(seq 1 100)
    do
        touch fichier_$i.txt
    done


Q21) Un peu de calculs

Le shell Bash est spécialisé dans la manipulation de fichiers et dossiers mais est capable de calculer un peu, essentiellement avec les entiers. Pour calculer il faut utiliser la commande "expr" (cf "man expr") qui permet d'évaluer des expressions, arithmétique ou sur les chaines de caractères. Pour nos besoins nous nous limiterons aux expressions arithmétiques dont la forme est la suivante :

Ecrire un script, nommé "successeur.sh", qui ajoute un au premier paramètre du script.


Réponse :

    #! /bin/bash

    RES=$(expr $1 + 1)
    echo Le successeur de $1 est $RES


Q22) Mélanger boucle et conditionnelle

Ecrivez un script, nommé "statistiques.sh", qui affiche le nombres de fichiers et de répertoires contenus dans le répertoire courant.


Réponse :

#!/bin/bash
NBFILE=0
NBDIR=0

for ELEM in $(ls)
do
  if test -f $ELEM
  then
      NBFILE=$(expr $NBFILE + 1)
  elif test -d $ELEM
  then
      NBDIR=$(expr $NBDIR + 1)
  fi
done

echo "There are $NBFILE files"
echo "There are $NBDIR directories"


Partie 6 - Expresssions rationnelle

Une expressions régulière ou expression rationnelle est une chaine de caractères qui décrit un ensemble de chaines de caractères. Elle utilise une syntaxe particulière pour décrire différentes classes de caractères qui composent l'ensemble de chaines de caractères. Il existe plusieurs types de langage d'expressions régulières, nous allons ici considérer un sous ensemble des ERE (Extended Regular Expression). La commande "grep" (Grind Regular Expression) est capable d'utiliser des expressions régulières pour faire de la recherche de chaines de caractères dans un fichier (cf "man grep").

Une expression rationnelle est composée :

Exemples :

A noter que les opérateurs * et + sont gourmands, c'est-à-dire qu'ils essaieront toujours de reconnaitre la chaine de caractères la plus longue.

Pour cette suite de questions, nous allons utiliser l'annuaire téléphonique qui est dans le fichier "annuaire.txt".

Q23) Trouvez toutes les entrées de l'annuaire dont le prénom est Mila


Réponse :

  grep -E "Mila" annuaire.txt

Note : pour activer l'utilisation des ERE dans grep il faut ajouter l'option -E.


Q24) Trouvez toutes les entrées de l'annuaire dont le numéro de téléphone termine par 1.


Réponse :

  grep -E "1$" annuaire.txt

Note : pour activer l'utilisation des ERE dans grep il faut ajouter l'option -E.


Q25) Trouvez toutes les entrées de l'annuaire dont le nom de famille commence par M.


Réponse :

  grep -E "^M" annuaire.txt


Q26) Trouvez toutes les entrées de l'annuaire dont le nom de famille commence par B et dont le numéro de téléphone termine par le chiffre 7.


Réponse :

  grep -E "^B.*7$" annuaire.txt


Q27) Trouvez toutes les personnes qui ont "Bertrand" comme nom de famille et dont le numéro de téléphone commence par "03"


Réponse :

  grep -E "Betrand.* 03" annuaire.txt

Note : attention à l'espace avant le 03


Q28) Trouvez toutes les entrées de l'annuaire dont le numéro de téléphone possède une répétition du numéro 3 supérieur à 2.


Réponse :

  grep -E "33+" annuaire.txt


Q29) Trouvez toutes les entrées de l'annuaire dont le prénom commence par un L et termine par un m.


Réponse :

  grep -E " L.+m " annuaire.txt

Note : attention aux espaces avant L ou après m sinon on peut avoir un nom de famille qui commence par L et un prénom qui finit par m

ou une autre solution

  grep -E "L[^ ]+m" annuaire.txt


Q30) Trouvez toutes les entrées de l'annuaire dont le nom de famille est "Durand" et dont le prénon est soit Jules, Louis ou Emma.


Réponse :

  grep -E "Durand (Jules|Louis|Emma)" annuaire.txt