Il existe de nombreux shells: sh, zsh, ksh, bash, csh, tcsh, etc. Leur syntaxe varie beaucoup. Les corrections de ce TP seront données pour le shell bash uniquement. Il est assez répandu (c'est le shell par défaut de GNU/Linux et MacOS, entre autres). Vous pouvez, bien sûr, faire le TP avec n'importe quel shell.
L'élève curieux pourra également se reporter au TP analogue proposé en 2007 par Antoine Miné.
Aide
Vous aurez probablement besoin des commandes Unix suivantes:
- man(1) pour comprendre l'utilisation de ces commandes! Le numéro entre parenthèse indique la section du manuel (option -s) où est documentée la commande (utile en cas d'homonymes). man man pour bien commencer.
- echo(1) pour écrire un message à l'écran ou dans un fichier (redirection avec > et >>),
- test(1) pour tester le type d'un fichier ou comparer des valeurs,
- sed(1) pour effectuer des substitutions de chaînes de caractères (commande s/.../.../g en particulier),
- grep(1) pour filtrer les lignes d'un fichier,
- sort(1) pour ordonner les lignes d'un fichier et éventuellement supprimer les doublons (option -u),
- tar(1) pour décompresser des archives .tar, .tar.gz, .tgz.
- md5sum(1) pour calculer et vérifier la valeur du haché MD5 d'un fichier
Vous pouvez également vous reporter à la documentation de bash, à bash(1) et au cours.
On rappelle enfin qu'un script shell est un fichier texte toto.sh qui commence par une ligne précisant le nom de l'interpréteur, par exemple:
(ou tout autre shell de votre choix) et qui est rendu exécutable par la commande chmod +x toto.sh. Il suffit alors de taper ./toto.sh pour l'exécuter. top
Exercice 1 - Affichage
-
Écrire le programme Hello World! en shell.
#!/bin/bash
echo "Hello world!" -
Écrire un script shell qui affiche les nombres premiers entre 2 et un entier n passé en argument.
(Crible d'Erathosthène)#!/bin/bash if [ "$#" = "0" -o "$1" = "-h" ]; then echo Usage : $(basename $0) n echo Affiche les premiers entre 2 et n. exit 1 fi EntierMax=$1 let FinCrible=EntierMax/2 TableauPremiers=('' '' $(seq 2 $EntierMax)) # Initialise un tableau d'entiers avec TableauPremiers[i]=i pour i>1 i=1 until (( ( i += 1 ) > FinCrible )) do if [[ -n $TableauPremiers[i] ]] then t=$i until (( ( t += i ) > EntierMax )) do TableauPremiers[t]='' done fi done echo ${TableauPremiers[*]} exit 0
-
Lorsqu'une commande prend en argument un chemin est que celui-ci n'existe pas, un message d'erreur est affiché, par exemple :
% ls /usr/lib/thissoft/config/Makefile
/usr/lib/thissoft/config/Makefile: No such file or directory.Écrire un script shell verif qui affichera la partie gauche du chemin qui est valide et la première entrée inexistante :
% verif /usr/lib/thissoft/config/Makefile
/usr/lib/thissoft: No such directory.#!/bin/bash if [ "$#" = "0" -o "$1" = "-h" ]; then echo "Usage : $(basename $0)
" echo "Affiche le prefixe valide de " exit 1 fi pos=`echo "$1/" | grep -o "/" | wc -l` # Compte le nombre de "/" dans $1 # (en en ajoutant un à la fin pour l'utilisateur etourdi) cpt=1 while [ "$cpt" -le "$pos" ] do dir=`echo $1 | cut -d / -f0-$cpt` if [ -d "$dir" ] ; then let "cpt += 1" else break fi done if [ "$cpt" -eq "1" ] then echo "pas de chemin valide" else cpt=$((cpt-1)) dir=`echo $1 | cut -d / -f0-$cpt` echo "$dir: repertoire valide" fi exit 0 -
Écrire un script shell permettant d'afficher l'arborescence d'un répertoire.
#!/bin/bash exploration () { for dir in * do if [ -d "$dir" ] ; then niveau=0 while [ $niveau != $prof ] # pour aligner do echo -n "| " let "niveau += 1" done echo "+---$dir" if cd "$dir" ; then let "prof += 1" exploration fi fi done cd .. if [ "$prof" ] ; then estfini=1 fi let "prof -= 1" } ###################################### if [ $# = 0 ] ; then cd `pwd` # repertoire courant si non specifie else cd $1 # sinon premier argument fi estfini=0 ; prof=0 ; niveau=0 while [ "$estfini" != 1 ] do exploration done exit 0
top
Exercice 2 - Gestion des fichiers
-
Écrire un script shell qui renomme les fichiers dont le nom contient une ou plusieurs espaces
(en remplaçant les espaces par _ par exemple).#! /bin/bash if [ $# = 0 ] ; then cd `pwd` # repertoire courant si non specifie else cd $1 # sinon premier argument fi for fichier in * do echo "$fichier" | grep -q " " # -q : grep n'écrit rien if [ $? -eq 0 ] then n=`echo $fichier | sed -e "s/ /_/g"` mv "$fichier" "$n" fi done exit 0
-
Écrire un script shell qui produit la liste de tous les fichiers de taille supérieure à n Mo (où n est un entier passé en argument).
#!/bin/bash if [ "$#" = "0" -o "$1" = "-h" ]; then echo "Usage : $(basename $0)
n" echo "Recherche fichiers de plus de n Mo dans ." exit 1 fi find $1 -type f -size +$2M # version simple # ou en plus joli : # find $1 -type f -size +$2M -exec ls -lh {} \; | \ # awk '{ print $8 ": " $5 }' -
Écrire un script shell qui recherche les fichiers en double dans un répertoire donné.
#!/bin/bash if [ "$#" = "0" -o "$1" = "-h" ]; then echo "Usage : $(basename $0)
" echo "Recherche les doublons dans ". exit 1 fi find $1 ! -empty -type f -printf "%s '%p'\n" | \ # cherche tous les fichiers du repertoire et affiche taille/nom sort -n | \ # tri par taille cut -d" " -f2- | \ # enleve la taille xargs md5sum | \ # calcule l'empreinte MD5 sort | \ # tri suivante l'empreinte MD5 uniq -w32 -d --all-repeated=separate | \ # affiche les lignes consecutives dont les # 32 premiers caractères sont identiques cut -c35- # affiche le resultat à partir du caractère 35 -
Écrire un script shell qui crée une archive .tag.gz de tous les fichiers modifiés pendant les dernières 24 heures.
#!/bin/bash cd $HOME; quand=$(date "+%y-%m-%d") cible=archive.${quand}; tar -zcvf "$HOME/$cible.tar.gz" \ `find . -mtime -1 -type f -print`
-
Écrire un script jeter qui permet de manipuler une poubelle à fichier - un répertoire - nommé poubelle et situé à votre racine. La commande accepte trois options:
- jeter -l pour lister le contenu de la poubelle ;
- jeter -t pour donner la taille de la poubelle ;
- jeter -s fichier chemin pour sortir le fichier fichier de la poubelle et le placer à l'emplacement chemin ;
- jeter -v pour vider la poubelle ;
- jeter fichier1 fichier2 ... pour déplacer les fichiers considérés vers la poubelle ;
#!/bin/bash if [ "$#" = "0" -o "$1" = "-h" ]; then echo Usage : $(basename $0) [-l|-s|-t|-v] [fichiers] exit 1 fi #! /bin/bash POUBELLE=$HOME/poubelle if test ! -d $POUBELLE then mkdir $POUBELLE fi case $1 in -l) echo "$POUBELLE :" ls $POUBELLE;; -s) shift echo "sortir fichiers $@ de la poubelle" mv "$POUBELLE/$@" ".";; -t) echo "taille de la poubelle" du -sk $POUBELLE;; -v) echo "la poubelle se vide..." rm -r $POUBELLE/*;; *) mv $@ $POUBELLE;; esac
top