Exercices en bin/sh

Christian Queinnec  
Université Paris 6 --- Pierre et Marie Curie  
LIP6, 4 place Jussieu, 75252 Paris Cedex -- France

Revision: 1.21

Introduction

Ce document contient une série d'exercices relatifs à l'interprète de commande bin/sh et à son environnement. En ce qui concerne /bin/sh, la meilleure documentation est celle que l'on obtient avec man sh. On pourra aussi lire avec fruit [Garfinkel94]. sh est important à connaître car c'est l'interprète de commande universel, celui que make utilise, celui qui a introduit les mécanismes que tous ses successeurs csh, tcsh, ksh, bash ... ont copiés.

NOTA: l'emploi direct de bash simplifierait la solution de certains de ces exercices. L'interprète de commande bash a en effet des possibilités d'arithmétique et de filtrage directs qui rendent inutile le recours à expr ou basename. Une prochaine édition comblera ce manque.

Ces exercices sont diffusés sous la licence Gnu Free Documentation License.

Auteur(s): Christian.Queinnec@lip6.fr.Mainteneur de la page: Christian.Queinnec@lip6.fr.

Pages suggérées: ABC d'Unix


Exercice: tiret.sh

 Auteur et droits


Écrire un script qui sort sur son flux de sortie tous ses arguments si l'un au moins d'entre eux débute par un tiret. Si aucun argument n'a de tiret, le code d'erreur retourné est différent de 0. Cette commande peut user de case pour analyser ses arguments.


Solution: 0 Noter le X qui permet de tester un argument vide comme l'est, par exemple, "". Différencier aussi $@ de $*. Noter enfin le : qui est l'instruction vide.
()
#! /bin/sh 

for word in "$@"
do
    case X$word in
    X-*)    echo "$@"
            exit 0
            ;;
    *)      :
            ;;
    esac
done    

exit 1

# end of tiret.sh


Auteur(s): Christian.Queinnec@lip6.fr.Mainteneur de la page: Christian.Queinnec@lip6.fr.


Exercice: somme.sh

 Auteur et droits


Écrire un script émettant, sur son flux d'erreur, la somme de tous ses arguments numériques. Zéro (l'élément neutre de l'addition) sera émis en cas d'absence d'argument numérique. On prendra en compte les nombres explicitement signés (par + ou -), on se limitera aux seuls entiers et l'on ignorera ce qui n'est pas un nombre. On utilisera avec profit la commande expr.


Solution: 0 Noter la redirection de la sortie normale sur le flux d'erreur, la suppression du signe + superflu d'au moins deux manières (trouvez-en encore une utilisant, par exemple, les possibilités de filtrage de la commande expr), le filtrage avec choix. Noter que l'expression [0-9]* ne filtre, pour sh, que les mots qui commencent par un chiffre et non les mots qui ne seraient composés que de chiffres.
()
#! /bin/sh

RESULT=0

for word in "$@"
do
    case X$word in
    X[0-9]*|X-[0-9]*)
        RESULT=`expr $RESULT + $word`
        ;;
    X+[0-9]*)
        #word=`IFS="+$IFS" ;  export IFS ;  eval echo "$word"`
        word=`echo $word | sed -e s/+//`
        RESULT=`expr $RESULT + $word`
        ;;
    esac
done

echo $RESULT 1>&2

# end of somme.sh
L'interprète de commande csh possède une arithmétique prédéfinie donc plus performante que l'appel à expr.

Auteur(s): Christian.Queinnec@lip6.fr.Mainteneur de la page: Christian.Queinnec@lip6.fr.


Exercice: which-a

 Auteur et droits


Écrire un script prenant un nom et cherchant dans quelle bibliothèque standard il est défini. Les bibliothèques sont des fichiers d'extension .a et sont usuellement en /usr/lib. On utilisera la commande nm pour connaître les noms définis par une bibliothèque. Le script s'arrête lorsque la bibliothèque est déterminée.


Solution: 0 Pour passer le temps, on émet les noms des bibliothèques scrutées.
()
#! /bin/sh

for lib in /usr/lib/*.a
do 
    echo "       Reading $lib ..."
    nm $lib | grep "T $1$" && exit 0
done

# end of which.a.

Auteur(s): Christian.Queinnec@lip6.fr.Mainteneur de la page: Christian.Queinnec@lip6.fr.

Pages suggérées: exercice which-h


Exercice: which-h

 Auteur et droits


Même exercice que sauf que l'on recherche maintenant dans quel fichier .h est défini un nom. Les fichiers .h apparaissent en /usr/include et ses sous-répertoires. Pour éviter que l'expansion *.h */*.h ne sature le nombre d'arguments permis, ces fichiers seront extraits par la commande:
find /usr/include -name '*.h' -print 


Solution: 0 J'ai, sur ma machine, 462 fichiers (comment les compteriez-vous?) ce qui excède, pour certains shells, le nombre d'arguments transmissibles. Noter l'usage du while.
(c)
#! /bin/sh

find /usr/include -name '*.h' -print | \
while read file
do
    grep "$1" $file
done

# end of which.h

Auteur(s): Christian.Queinnec@lip6.fr.Mainteneur de la page: Christian.Queinnec@lip6.fr.

Pages requises: exercice which-a.


Exercice: voir.sh

 Auteur et droits


Écrire un script prenant un nom de fichier en argument et le visualisant. Le programme de visualisation sera choisi en fonction du nom du fichier et plus particulièrement son extension. On traitera au moins les noms *.dvi, *.ps, README, *.gz. S'il n'y a pas de nom de fichier, on affichera avec more le flux d'entrée. Un fichier de texte comme README sera affichée par more dans une fenêtre indépendante.


Solution: 0 Noter la disparition de la fenêtre sous dix secondes, l'usage de sh sous xterm, l'emploi d'exec économisant un processus ainsi que la commande basename.
()
#! /bin/sh

case "X`basename $1`" in
    X*.dvi)
        xdvi $1
        ;;
    X*.ps)
        ghostview $1
        ;;
    XREADME)
        TIMEOUT=10
        xterm -e sh -c "more $1 ;  sleep ${TIMEOUT}"
        ;;
   X*.gz)
        TMP=/tmp/`basename $1 .gz`
        gunzip -c $1 > ${TMP}
        exec $0 ${TMP}
        rm -f ${TMP}
        ;;
   X)
        more
        ;;
   *)
        echo Cannot deal with $1
        exit 1
        ;;
esac

# end of voir.sh

Auteur(s): Christian.Queinnec@lip6.fr.Mainteneur de la page: Christian.Queinnec@lip6.fr.


Exercice: double-nom.sh

 Auteur et droits


Écrire un script prenant un répertoire en argument (par défaut le répertoire courant) et déterminant les fichiers apparaissant au moins deux fois dans cette hiérarchie. La comparaison ne prendra en compte que le seul nom: deux fichiers seront considérés comme doublons s'ils ont le même nom. On utilisera les commandes sort et uniq.


Solution: 0
()
#! /bin/sh

find ${1-`pwd`} -print | \
while read f ;  do basename $f ;  done | \
sort | \
uniq -d

# end of double-nom.sh
En guise d'exercices complémentaires, comparer les fichiers d'après leur contenu.

Auteur(s): Christian.Queinnec@lip6.fr.Mainteneur de la page: Christian.Queinnec@lip6.fr.


Exercice: pgrep.sh

 Auteur et droits


Écrire un script prenant en argument un motif et des noms de fichiers et agissant comme grep c'est-à-dire recherchant les lignes des fichiers contenant le motif (une expression régulière). L'originalité est que l'on désire que ces recherches soient effectuées en parallèle. Le degré de parallélisme sera fixé à quatre: c'est-à-dire que l'on souhaite lancer jusqu'à quatre grep en parallèle.


Solution: 0 Noter l'usage d'eval, de shift ainsi que la présence de la variable MAX permettant de paramétrer le nombre de grep à lancer en parallèle. Noter aussi l'usage de [], autre nom de la commande test.
()
#! /bin/sh

FILES0=
FILES1=
FILES2=
FILES3=

MOTIF=$1
shift

N=0
MAX=4

for f
do
    eval "FILES$N=$FILES$N $f"
    N=`expr $N + 1`
    if [ $N = $MAX ] ;  then N=0 ;  fi
done

N=0

until [ $N = $MAX ]
do
    eval grep ${MOTIF} $FILES$N &
    N=`expr $N + 1`
done

# end of pgrep.sh

Auteur(s): Christian.Queinnec@lip6.fr.Mainteneur de la page: Christian.Queinnec@lip6.fr.


Exercice: slideshow.sh

 Auteur et droits


Écrire un script prenant une série de fichiers contenant des images et les affichant une par une en fond d'écran. Le fond d'écran sera changé une fois par minute (temps configurable, bien sûr) ou lorsque le script recevra un signal C (utiliser alors l'instruction trap). Le script stockera son numéro de processus dans un fichier afin de pouvoir être tué par un kill -9.


Solution: 0 Noter que DURATION peut déjà appartenir à l'environnement. Le script tente de rattraper la majeure partie des signaux.
()
#! /bin/sh

DURATION=${DURATION-60}

echo $$ > slideshow.pid

for image
do
    xloadimage $image & 
    trap "kill -9 $!" 1 2 3 4 5 6 7 8 10 11 12 13 14
    trap "kill -9 $! $$" 15
    sleep $DURATION 
    kill -9 $!
done
exec $0 "$@"

# end of slideshow.sh

Auteur(s): Christian.Queinnec@lip6.fr.Mainteneur de la page: Christian.Queinnec@lip6.fr.





Ce document a été traduit de LATEX par HEVEA.