Exercices bash

1. Remplacements

Objectifs : Manipuler les chaines de caractères, comprendre la notion d'entrée et de sortie standard
Durée : 5min
Difficulté : Facile

Consigne
Ecrire une commande qui permet d'écrire le résultat d'une commande echo dans un fichier texte en remplacant tous les espaces qu'il contient par des _

echo 'un texte' | tr ' ' '_' > ./fichier.txt

2. Minimum et maximum

Objectifs : Manipuler les principales structures, effectuer des comparaisons
Durée : 30min
Difficulté : Moyen

Consigne
Créer deux fonctions : la première cherche et affiche le minimum d'une liste de nombre passée en paramètre. La deuxième affiche le maximum.

#/bin/ksh
# Cette fonction permet de factoriser le code des fonctions 
# de calcul du min et du max
function recherche {
	res=$1
	for nombre in $*
	do
		(( $nombre $symbole $res )) && res=$nombre
	done
	echo $res
}

# Cette fonction affiche le minimum de la chaine en paramètre
function min {
	symbole='<'
	recherche $*
}

# Cette fonction affiche le maximum de la chaine en paramètre
function max {
	symbole='>'
	recherche $*
}

# Cette fonction affiche le minimum de la chaine en paramètre
function min {
	echo $* | tr ' ' '\n' | sort -n | head -1
}
 
# Cette fonction affiche le maximum de la chaine en paramètre
function max {
	echo $* | tr ' ' '\n' | sort -n | tail -1
}

3. Créer une corbeille

Objectifs : Créer des fonctions, Manipuler les fichiers
Durée : 30min
Difficulté : Difficile

Consigne
Le but de cet exercice est de réaliser un mécanisme de corbeille qui permet de supprimer un fichier et de le restaurer. Pour cela :

  • Créer une fonction del qui permet d'archiver le fichier passé en paramètre dans un dossier que l'on appelera la corbeille et qui surppime le fichier.
  • Créer une fonction undel qui restaure le fichier archivé et qui supprime l'archive de la corbeille.
  • Créer une fonction listdel qui liste les fichiers de la corbeille.
  • Créer une fonction purgedel qui vide la corbeille.

#!/bin/ksh
# Emplacement de la corbeille
corbeille=/tmp/$USERNAME/corbeille

# Cette fonction permet d'archiver un fichier
# $1 chemin complet du fichier à archiver
function del {
	[[ ! -e $corbeille ]] && mkdir -p $corbeille
	[[ -e $1 ]] && tar -cvf $corbeille/$1.tar $1 && rm -r $1 
}

# Cette fonction restaure un fichier 
# $1 nom du fichier à restaurer
function undel {
	if [[ -e $corbeille/$1.tar ]]
	then
		tar -xvf $corbeille/$1.tar
		rm $corbeille/$1.tar
	fi
}

# Cette fonction liste les fichiers de la corbeille
function listdel {
	ls $corbeille
}

# Cette fonction vide la corbeille
function purgedel {
	for fichier in $(ls $corbeille)
	do
		rm $corbeille/$fichier
	done
}

4. Deviner un nombre

Objectifs : Manipuler les principales structures, effectuer des comparaisons, lire l'entrée standard
Durée : 30min
Difficulté : Moyen

Consigne
Créer un jeu qui permet à l'utilisateur de deviner un nombre compris entre 0 et 100 en effectuant moins de 10 propositions. Pour chaque proposition, le système indique à l'utilisateur si le nombre qu'il a proposé est trop petit ou trop grand.

(( alea = $RANDOM % 101 ))
i=1
echo "Essayez de deviner le nombre (entre 0 et 100)"
while [[ $i -le 10 ]]
do
	read proposition
	[[ $proposition -lt $alea ]] && echo trop petit
	[[ $proposition -gt $alea ]] && echo trop grand
	[[ $proposition -eq $alea ]] && echo Gagne && exit 0
	echo "Essayez encore"
	(( i++ ))
done
echo "Perdu ! le nombre etait $alea"

5. Nombre de fichiers et de dossiers d'un répertoire

Objectifs : Manipuler le FileSystem, itérer sur des fichiers
Durée : 10min
Difficulté : Facile

Consigne
Réaliser un script qui permet de compter le nombre de fichiers et de répertoires dans un répertoire donné. Le résultat est affiché dans la sortie standard

#/bin/ksh

repertoire=$1
nbRep=0
nbFic=0
for element in $repertoire/*
do
	[[ -d $repertoire/$element ]] && (( nbRep++ ))
	[[ -f $repertoire/$element ]] && (( nbFic++ ))
done
echo $nbRep repertoires et $nbFic fichiers

repertoire=$1
ls -l $repertoire | grep '^d.*' | wc -l

6. Nombres aléatoires pairs

Objectifs : Manipuler les principales structures, faire des calculs
Durée : 15min
Difficulté : Facile

Consigne
Réaliser un script qui permet de générer dans la sortie standard 20 nombres aléatoires pairs. Indication : la variable $RANDOM contient un nombre aléatoire

i=0
while [[ $i -lt 20 ]]
do
	alea=$RANDOM
	(( $alea % 2 == 0 )) && echo $alea && (( i++ ))
done

7. Numéroter les lignes d'un fichier

Objectifs : Lire l'enstrée standard
Durée : 15min
Difficulté : Moyen

Consigne
Le but de cet exercice est de numéroter les lignes d'un fichier qui est lu dans l'entrée standard. Il y a deux facons d'appeler le script : soit le nom du fichier est pris en argument soit l'entrée standard est utilisée.

#!/bin/ksh

# Cette fonction permet de numéroter les lignes lues 
# dans l'entrée standard
function numeroter {
	i=0
	while read a
	do
		(( i++ ))
		echo $i" "$a
	done
}
if [[ $# -ne 0 ]]
then
	numeroter < $1
else
	numeroter
fi

function numeroter {
	awk '{print NR" "$1}'
}

8. Expressions régulières

Objectifs : Comprendre la syntaxe des expressions régulières
Durée : 10min
Difficulté : Facile

Consigne
Donner le motif qui correspond à une adresse IP. Une adresse IP est de la forme suivante : 255.255.0.1 ou bien 127.0.0.1, son format est donc le suivant : Un à trois chiffres, point, un à trois chiffres, point, un à trois chiffres, point, un à trois chiffres.

[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}

Consigne
Donner l'expression régulière qui correspon à une date sous la forme « 29 février 2012 ». Une date au format 29 février 2012 suit le motif suivant : un ou deux chiffres, un espace, un nombre indéfini de lettres, un espace puis quatre chiffres.

[0-9][0-9] [a-z][a-z]* [0-9][0-9][0-9][0-9]

9. Outil de terminal serveur

Objectifs : Utiliser le système de fichier, manipuler les principales structures, gérer les droits
Durée : 1h
Difficulté : Difficile

Partie 1 : le client
Nous allons créer un outil permettant à une utilisateur connecté sur un poste de travail A d'exécuter des commandes sur un poste de travail B. Pour cela, on commence par créer un dossier partagé dans lequel les informations seront échangées.

  • Le client attend les instructions de l'utilisateur
  • Une fois une instruction entrée, il créé un fichier contenant la commande a éxécuter dans le répertoire d'échange (fichier nommé actions_<idep>.ksh)
  • Le client se met en attente du fichier reponse_<idep>.txt
  • Une fois ce fichier arrivé, le client affiche son contenu et le supprime
  • Enfin, il se remet en attente des insctructions de l'utilisateur

#/bin/ksh

dossier_echange=/net/S50F-KSH-GGA/Users/k0b6qu/Desktop/echange
ident=k0b6qu

while read commande 
do 
	echo $commande > $dossier_echange/actions_$ident.ksh
	while [[ ! -e $dossier_echange/reponse_$ident.txt ]]
	do
		sleep 1
	done
	cat $dossier_echange/reponse_$ident.txt
	rm -f $dossier_echange/reponse_$ident.txt
done

Partie 2 : le serveur

  • Le serveur attent qu'un fichier actions_<idep>.ksh soit créé dans le répertoire d'échange
  • Une fois que ce fichier est créé, il exécute la commande contenue dans ce fichier, et enregistre le résultat de la commande dans le répertoire échange sous la forme : reponse_<idep>.txt
  • Il supprime le fichier actions_<idep>.ksh et se remet en attente de ce fichier

#/bin/ksh

# Emplacement du dossier partagé
dossier_echange=/net/S50F-KSH-GGA/Users/k0b6qu/Desktop/echange

# Identifiant des fichiers à traiter
ident=k0b6qu

# Fonction permettant de traiter une commande dans le fichier
# texte $dossier_echange/actions_$ident.ksh
function traiter_commande {
	reponse=$dossier_echange/reponse_$ident.txt
	$dossier_echange/actions_$ident.ksh > $reponse 2>&1
	rm -f $dossier_echange/actions_$ident.ksh
}

commande=$dossier_echange/actions_$ident.ksh
while true
do
	[[ -e  $commande ]] && traiter_commande
	sleep 1
done

10. awk : taille moyenne, nombre de fichiers

Objectifs : Utiliser awk, rediriger l'entrée standard
Durée : 30min
Difficulté : Difficile

Consigne
Déterminier avec awk la taille moyenne et le nombre de fichiers d'un répertoire donné. Le script awk à écrire sera placé derrière la commande ls -l.

BEGIN{
	i=0;
	n=0;
}
{
	n=n+$9;
	i=i+1;
}
END{
	printf "%d fichiers\nTaille moyenne : %.2f\n", i, n/i;
}

11. Jeu type Motus

Objectifs : Utiliser les structures, lire l'entrée standard, manipuler les chaines de caractèrs
Durée : 1h30
Difficulté : Difficile

Consigne
Réalisez un jeu qui permet à l'utilisateur de découvrir un mot comme dans motus:

  • Le système affiche uniquement la première lettre du mot et remplace les autres lettres par des «_». Exemple avec le mot MAISON : M _ _ _ _ _
  • Le système propose alors à l'utilisateur d'entrer une propostion.
  • Le système compare la proposition de l'utilisateur avec le mot mystère, toutes les lettres de la proposition qui sont bien placées sont alors affichées : par exemple M _ I S _ _ et le système propose alors à l'utilisateur d'entrer un nouveau mot.

#
# Ce script permet de lancer un jeu "Motus"
# Parametre 1
#	Le mot à trouver
#

# La chaine de caractères à trouver
atrouver=$1

#
# Fonction permettant de comparer une proposition à atrouver
# La chaine de caractères en_cours est actualisée par la fonction
# Parametre 1
# 	Proposition à comparer à atrouver
# Retour
#	0 si les chaines de caractères sont identiques
#	1 sinon
#
function verifier {
	res=0
	encours=$(echo $atrouver | cut -c 1)
	i=2
	while [[ $i -le ${#atrouver} ]]
	do
		l1=$(echo $atrouver | cut -c $i)
		l2=$(echo $1 | cut -c $i)
		if [[ "$l1" == "$l2" ]]
		then
			encours=$encours" $l1"
		else
			encours=$encours" _"
			res=1
		fi
		(( i++ ))
	done
	return $res
}

verifier

clear
while true
do
	echo $encours
	echo "Entrez votre proposition"
	read prop
	clear
	verifier $prop
	[[ $? -eq 0 ]] && echo Gagne && break
done

12. Messagerie instantanée

Objectifs : Utiliser les structures, réaliser des sémaphores, accéder au système de fichiers
Durée : 1h
Difficulté : Difficile

Consigne
Réaliser une application de messagerie instantanée. Deux programmes tourneront en parallèle :

  • le permier permettra d'ajouter une ligne saisie par l'utilisateur dans un fichier texte (en s'assurant qu'aucun autre utilisateur n'écrit dans le fichier texte)
  • le second permettra d'afficher le fichier texte en le mettant à jour automatiquement lorsque celui-ci aura été modifié.

lastmodif=0
fichier_conversation=/dev/fs/D/messagerie/conversation.txt
while true
do
	modif=$(ls $fichier_conversation -l | cut -d " " -f 9)
	if (( $modif == $lastmodif ))
	then
		sleep 1
	else
		clear
		cat $fichier_conversation
		lastmodif=$modif
	fi
done

fichier_conversation=/dev/fs/D/messagerie/conversation.txt
while true
do
	read a
	while [[ -e /dev/fs/D/samuelestlemeilleur/.LOCK ]]
	do
		sleep 1
	done
	touch /dev/fs/D/messagerie/.LOCK
	echo "Julien dit: $a" >> $fichier_conversation
	rm -f /dev/fs/D/messagerie/.LOCK
done

13. Tracer une courbe

Objectifs : Utiliser les principales structures awk
Durée : 1h
Difficulté : Difficile

Consigne
Le but de cet exercice est de tracer la courbe du cosinus dans une fenêtre shell comme dans l'exemple ci dessous avec l'axe des abscisses vertical : Pour cela, nous allons utiliser awk.

Il faudra penser à définir :

  • Le pas de l'intervalle (0.15)
  • Les bornes inférieures et supérieures (0 et 500)
  • Le multiplicateur pour le cosinus (40 la courbe tracée sera 40*cos)
Il faudra traiter différement les cas où la valeur est positive des cas où elle est négative. Pour avoir le temps de voir la courbe se tracer, on peut utiliser l'instruction system("sleep 1") Enfin, l'axe des abscisses est tracé avec le symbole | et la courbe est tracée à l'aide du symbole *

Courbe cosinus sous awk
BEGIN{
	pas=0.15;
	borneinf=0;
	borneSup=500;
	multiplicateur=40;
	for(i=borneinf;i*pas+borneinf<borneSup;i++) {
		system("sleep 1");
		x=borneinf+pas*i;
		chaine="";
		if(multiplicateur*cos(x)>0) {
			for(j=0; j<multiplicateur;j++) {
				chaine=sprintf("%s ", chaine);
			}
			chaine=sprintf("%s|", chaine);
			for(j=0;j<multiplicateur*cos(x);j++){
				chaine=sprintf("%s ", chaine);
			}
			chaine=sprintf("%s*", chaine);
		}
		else{
			for(j=-multiplicateur;j<multiplicateur*cos(x);j++){
				chaine=sprintf("%s ", chaine);
			}
			chaine=sprintf("%s*", chaine);
			for(j=multiplicateur*cos(x); j<-2;j++){
				chaine=sprintf("%s ", chaine);
			}
			chaine=sprintf("%s|", chaine);
		} 
		print chaine;
	}
}

14. Affichage de fichier

Objectifs : Créer une commande, manipuler le système de fichier, créer des options
Durée : 45min
Difficulté : Moyen

Consigne
Créer un script qui permet d'afficher un fichier passé en paramètre. Ce script reconnaitra les options suivantes :

  • -n : les lignes seront numérotées
  • -h nb : seules les nb premières lignes seront affichées

#!/bin/ksh
# Ce script permet d'afficher un fichier texte
# Options
# -n
#	Numérote les lignes du fichier
# -h nb
# 	Affiche les nb premières lignes du fichier
# Paramètres
# paramètre 1
#	Le fichier texte à afficher

echo $* | grep -q '\-n' && numerotation=ok
debut=$(echo $* | grep '\-h' | sed 's/.*-h *\([0-9]*\).*/\1/')

fichier=$(echo $* | sed 's/-n//;s/-h *[0-9]*//;s/ +/ /g;s/^ *//')
[[ ! -e $fichier ]] && echo Le fichier n\'existe pas >&2 && exit 1

# Cette fonction permet de numéroter les lignes
# de l'entrée standard
function numeroter_lignes {
	i=1
	while read l
	do
		echo "$i : $l"
		(( i++ ))
	done
}

commande="cat"
[[ ! -z $lignes_deb ]] && commande="head -$debut" 
commande="$commande $fichier"
[[ "$numerotation" = "ok" ]] && $commande | numeroter_lignes && exit 0
$commande && exit 0
exit 1

15. Erreurs à corriger

Objectifs : Identifier les erreurs de développement
Durée : 20min
Difficulté : Facile

Maladresse 1

cat fichier.txt | grep '7:'

#Le pipe ne sert à rien, la redirection de l'entrée se fait sur un fichier et non sur un processus.
grep '7:' < fichier.txt

Maladresse 2

for f in $(cat file)
do
	commande "$f"
done

#La commande cat permet de concaténer les fichiers, pas de les lire. Pour lire un fichier, on le place dans l'entrée standard
while read f
do
	commande "$f"
done < file

Maladresse 3

if [ $S_ESPEXE = I ]
then
	partage_ihm=Recette
else
	if [ $S_ESPEXE = A ]
	then 
		partage_ihm=Developpement
	else
		if [ $S_ESPEXE = S ]
		then
			partage_ihm=Integration
		else
			partage_ihm=Production
		fi
	fi
fi

#Le nombre de test à réaliser est trop important, le programme sera long à cause des boucles imbriquées, et on ne comprend pas facilement ce qui se passe. Il y a plusieurs facons de corriger les erreurs.
partage_ihm=Production
[[ $S_ESPEXE == I ]] && partage_ihm=Recette
[[ $S_ESPEXE == A ]] && partage_ihm=Developpement
[[ $S_ESPEXE == S ]] && partage_ihm=Integration

case $S_ESPEXE in
	I ) partage_ihm=Recette ;;
	A ) partage_ihm=Developpement ;;
	S ) partage_ihm=Integration ;;
	X ) partage_ihm=Production ;;
esac

Maladresse 4

ps -l | grep -v 'STATE' | awk '{print $2}' 

#Awk permet de réaliser des filtres sur les lignes d'un fichier, il n'y a donc pas de raison d'utilsier un grep.
ps -l | awk '!/ *STATE.*/{print $2}'

Maladresse 5

admsnap activate -s $sess | grep hdiskpower | awk '{print $6}' | sed 's/\/dev\/r//' | sed 's/\.//' 

la commande admsnap activate -s $sess est à remplacer par echo 1 2 3 4 5 /dev/r/hdiskpower12.1

#Trop d'instructions s'enchainent, et bien qu'il n'y ait pas d'erreur grossière, le temps de traitement est très fortement impacté.
admsnap activate -s $sess | grep hdiskpower | cut -d ' ' -f '6' | sed 's/\/dev\/r//' | tr -d '.'
# ou
admsnap activate -s $sess | awk '/.*hdiskpower.*/{gsub("/dev/r/", "", $6);print $6;}'
# ou
admsnap activate -s $sess | sed 's/.*\(hdiskpower[0-9]*\)\.\([0-9]*\)/\1\2/g'

Maladresse 6

cd $repertoire
while true
do
echo entrer le nom du fichier à afficher
read fichier
	cat $repertoire/$fichier
done

# Le cd est inutile, et les paramètre doivent être entourés par des guillemets au cas où l'utilisateur entre un nom de fichier avec des espaces.
while true
do
echo entrer le nom du fichier à afficher
read fichier
	cat "$repertoire/$fichier"
done

Maladresse 7

for f in $(ls *.txt)
do
	cat $f
done

# *.txt est expansé directement, pas besoin d'utiliser de ls ou de echo.
for f in *.txt
do
	cat "$f"
done

Maladresse 8

function f {
	...
	cd /dev/fs/D/dossier
}

# On ne réalise pas de cd dans une fonction pour ne pas modifier les informations du processus courant. Si on fait un cd, on restaure à l'état initial.
# Mais le mieux est sans doute de n'utiliser dans les fonction (et de manière générale) que des chemins absolus.
# Le cd est une simplification pour l'utilisateur, mais n'est pas indispensable dans les scripts.
function f {
	...
	rep_courant=$(pwd)
	cd /dev/fs/D/dossier
	cd $rep_courant
}

Maladresse 9

ls $dossier | head –1 | read var

# Le pipe vers la commande read ne fonctionne pas en kornshell sous SUA, certains shell acceptent cette syntaxe.
var=$(ls $dossier | head –1)

Maladresse 10

fichier="/dev/fs/C/fichier 1.txt"
ls $fichier

# Il ne faut pas oublier les guillemets autour de $fichier dans ce cas. Même s'ils protègent l'affectation, ils ne se retrouvent pas dans la ligne suivante
fichier="/dev/fs/C/fichier 1.txt"
ls "$fichier"

Maladresse 11

commmande | grep '..*' | wc -l

# L'option -c permet de compter le nombre de motifs intercéptés par grep.
commmande | grep -c '..*'

16. Quizz sur les étapes d'interprétation

Objectifs : Étapes d'interprétation de la ligne de commande
Durée : 20min
Difficulté : Difficile

Que se passe-t-il 1 ?

alias f="echo alias"
function f {
	echo fonction
}
f

alias est affiché. En effet, le traitement des alias est antérieur à celui des fonctions, f est donc d'abord remplacé par « echo alias ».

Que se passe-t-il 2 ?

alias ls=ps
alias ll="ls -l"
ll

ps -l est exécuté. Les alias sont traités entièrement, tant qu'il existe un alias dans la ligne de commande, celui-ci est résolu.

Que se passe-t-il 3 ?

alias a=ls
function ls {
	echo fonction
}
a

fonction est affiché. Lors de l'appel de f, l'alias est traité en premier, f est remplacé par fonction. Lors de la localisation de la commande a effectuer, fonction est trouvée dans la liste des fonctions en mémoire et est exécutée.

Que se passe-t-il 4 ?

function f {
	echo a
}
alias a=ls
$(f)

Une erreur est générée, a n'est pas reconnu en tant que commande interne. Lors de la détermination du type de commande, le traitement des alias a déjà été effectué. Voir les étapes d'intérprétation de la ligne de commande.

Que se passe-t-il 5 ?

alias a=ls
var=a
$var

Une erreur est générée, l'intéprétation des variables se fait après le traitement des alias.

Que se passe-t-il 6 ?

alias a='$var'
var=ls
a

ls est exécuté.

Que se passe-t-il 7 ?

script1.ksh
echo script1
exit 0
Dans un autre script :
./script1.ksh

script1 est affiché dans la console

. ./script1.ksh
script1 est affiché dans la console et on sort de l'interpréteur.

Que se passe-t-il 8 ?

var1=ok
var2='$var1'
echo $var2

Les variables ne sont intérprétées qu'une seule fois, $var1 est affiché. Pour afficher le contenu de var1, on utiliserait

eval echo $var2
Dans ce cas, l'évaluation des variables est faite deux fois.

Que se passe-t-il 9 ?
script1.ksh

cd /
Dans l'invite de commande :
cd /etc
script1.ksh
pwd

pwd affiche /etc, le cd effectué dans Script1.ksh est fait dans un autre processus que le processus courant.

Que se passe-t-il 10 ?

var="> fichier.txt"
echo texte $var

texte > fichier.txt est affiché dans la console, La mise en place des redirections ne signifie pas que la redirection est lue sur la commande, la redirection est récupérée sur au moment du découpage de la ligne en mots, le symbole > est considéré comme un séparateur.si un > se retourve encore sur la ligne de commande au moment de la mise en palce des redirections, il n'est pas pris en compte comme le symbole de redirection mais comme un caractère quelconque. S'il n'est pas présent au départ sur la ligne, la redirection ne peut être mise en place.

Que se passe-t-il 11 ?

alias var='ok'
echo var 

var est affiché, l'alias n'est remplacé que lorsqu'il s'agit d'une commande.

17. Analyse d'un fichier avec awk

Objectifs : Utiliser awk, traiter un fichier, utiliser les expressions régulières
Durée : 1h
Difficulté : Difficile

Consigne
A l'aide du fichier annuaire.txt suivant :

Prénom NOM Date-de-naissance Sexe Numéro-de-telephone
Georges TARTEMPION 12 02 1967 M 038898422524
Adrien DUBOUCHON 27 11 1938 M 0154896372
Ludwig SCHMILLBLEERCK 28 12 1957 M 0258459887
Antonio VAVILDA 16 05 1937 M
Hughes CARPETTE 08 11 1958 M 0123568629
J'en ai assez de ce travail fastidieux!
Dagobert ELOY 14 07 1947 M 0225415487

Anatole SUISSE 01 02 1965 n 4
Antonino SZWPRESWKY 16 05 8937 M 0298358745
Créer un script awk qui créé un annuaire au format html :
<h1>Annuaire</h1>
<p>
<b>Mr Georges TARTEMPION</b>
<br />
né le : 12/02/1967
<br />
tel : 038898422524
<br />
mail : georges.tartempion@insee.fr
</p>
Les lignes vides ou mal formés ne seront pas affichées.

awk '
BEGIN{
	print "<html><body><h1>Annuaire</h1>";
}
/[A-Z][a-z]+ [A-Z]+ [0-9]+ [0-9]+ [0-9]+ M|F [0-9]+/{
	printf "<p>\n<b>";
	if($6 == "M"){
	printf "Mr ";
	}
	else {
		printf "Mme ";
	}
	printf "%s %s</b>\n<br />", $1, $2;
	printf "ne le : %d/%d/%d\n<br />", $3, $4, $5;
	printf "tel : %s\n<br />", $7;
	printf "mail:%s.%s@insee.fr</p>\n", tolower($1), tolower($2);
 
}
END{
	print "</body></html>";
}'

18. Analyse d'un fichier avec sed

Objectifs : Utiliser sed, taiter un fichier, utiliser les expressions régulières
Durée : 1h
Difficulté : Très difficile

Consigne
Même exercice que Analyse d'un fichier avec awk mais avec sed sauf la gestion des minuscules/majuscules (trop long pour être traité)

# Appel de sed sed -n -f commandes_sed.txt annuaire.txt

# Le script sed correspondant :
1, /[A-Z][a-z]* [A-Z]* [0-9]* [0-9]* [0-9]* [MF] [0-9]*/s!^!<html><body><h1>Annuaire</h1>!g
/[A-Z][a-z]* [A-Z]* [0-9]* [0-9]* [0-9]* [MF] [0-9]*/!d

s/\([A-Z][a-z]* [A-Z]* [0-9]* [0-9]* [0-9]* \)M\( [0-9]*\)/\1Mr\2/g

s/\([A-Z][a-z]* [A-Z]* [0-9]* [0-9]* [0-9]* \)F\( [0-9]*\)/\1Mme\2/g

s!\([A-Z][a-z]*\) \([A-Z]*\) \([0-9]*\) \([0-9]*\) \([0-9]*\) \(M[mr][e]*\) \([0-9]*\)!<p><b>\6 \1 \2</b><br />ne le : \3/\4/\5<br />tel : \7<br />mail : \1.\2@insee.fr</p>!g

p

19. Jeu de nim

Objectifs : Manipuler les chaines de caractère, manipuler les principales structures, lire l'entrée standard
Durée : 1h
Difficulté : Difficile

Consigne
Créer un script qui permet de jouer au jeu de nim : Quinze alumettes sont posées, chaque joueur à tour de rôle choisit de 1 a trois alumettes et les retire. Le joueur qui tire la dernière alumette a perdu.
Créer une fonction qui permet d'afficher autant de « | » que d'alumettes restantes. Au démarrage d'une partie le nombre d'alumettes est initialisé à 15 dans une variable alumettes_resantes. Créer une fonction qui permet de retirer de une a trois alumettes de ce nombre, le nombre à retiré est passé en paramètre. Créer une fonction qui permet a l'ordinateur de retirer un certain nombre d'alumettes selon la règle suivante : une fois que l'ordinateur a joué, le reste de la division euclidienne du nombre d'alumettes restantes par 4 devra être 1. Si cela n'est pas possible, l'ordinateur prend 3 alumettes

#
# Fonction qui affiche autant de batons que d'alumettes restantes
# Parametre 1 : le nombre de batons à afficher
#
function afficher_reste {
	i=0
	a_afficher=""
	while (( $i < $1 ))
	do
		a_afficher="| "$a_afficher
		(( i++ ))
	done
	echo $1 alumettes restantes :
	echo $a_afficher
}

#
# Fonction qui permet de prendre des alumettes
# Parametre 1 : nombre d'alumettes à prendre
#
function prendre {
	if (( $1 < 4 ))
	then
		(( alumettes_resantes-=$1 ))
	else
		echo vous ne pouvez pas prendre plus de 3 alumettes
	fi
}

#
# Fonction qui permet de faire jouer l'ordinateur
#
function jeu_systeme {
	a_prendre=3
	(( \( $alumettes_resantes - 1 \) % 4 == 1 )) && a_prendre=1
	(( \( $alumettes_resantes - 2 \) % 4 == 1 )) && a_prendre=2
	echo le systeme prend $a_prendre alumettes
	prendre $a_prendre
}

#
# Fonction de lancement d'une nouvelle partie
#
function nouvelle_partie {
	alumettes_resantes=15
	while (( $alumettes_resantes > 1 ))
	do
		afficher_reste $alumettes_resantes
		echo combien vous vous en prendre ?
		read choix
		prendre $choix
		if [[ $alumettes_resantes -ne 1 ]]
		then
			sleep 1
			jeu_systeme
			(( $alumettes_resantes==1 )) && echo Vous avez perdu 
		else 
			echo vous avez gagne
		fi
	done
}

20. Gestion des processus

Objectifs : Gerer les processus, utiliser les signaux
Durée : 1h
Difficulté : Difficile

Partie 1. Création du script
Créer un script qui incrémente une valeur toute les 5 secondes et affiche la valeur dans un fichier texte de sortie. Ce script peut être interrompu a l'aide d'un Ctrl+C, mais dans ce cas là, le fichier de sortie doit être supprimé. Lorsqu'une réinitialisation est demandée, le compteur repart à 0.

trap 'rm -f $sortie' INT
trap 'i=0' HUP
sortie=sortie.txt
i=0
while true
do
	sleep 5
	echo $i > $sortie
	(( i++ )) 
done

Partie 2. Lancement du processus
Lancez le processus en arrière plan et tester la réinitialisation et l'arrêt avec Ctrl+C (sans utiliser de kill).

./compeur.ksh &
kill -HUP %1 &
jobs
	[1] + Running	 ./compteur.ksh
fg %1
Ctrl+C

21. Chat avec interface Graphique

Objectifs : Gestion des processus, appel de java, notion de sémaphores
Durée : 45min
Difficulté : Difficile

Consigne
Le fichier chat.jar contient une interface graphique qui permet de lancer une application de chat. Deux exécutables sont dans ce jar :

  • AffichageFichier permet d'afficher la conversation ; le path vers le fichier contenant la conversation est donné en argument au programme.
  • ChampTexte permet de demander à l'utilisateur d'entrer un texte. Lorsque le texte est entré par l'utilisateur, celui-ci est affiché dans la sortie standard. Je nom d'utilisateur est passé à l'aide d'un -DnomUtilisateur.
Ecrire un script shell qui permet de piloter ces deux applications pour réaliser une application de chat. Il faudra veiller à détruire tous les processus créés par ce script.

set -x
trap 'kill -9 $ancienPid; kill -9 $pidChampTexte; exit 0' INT KILL

fic=/home/messagerie/conversation.txt
> $fic_conv

java -DnomUtilisateur=Julien -jar ChampTexte.jar >> $fic_conv &
pidChampTexte=$!

nbLignes=0
while true
do
	nb=$(wc -l < $fic_conv)
	if [[ $nb -ne $nbLignes ]]
	then
		nbLignes=$nb
		[[ -n $ancienPid ]] && kill -9 $ancienPid
		java -jar AffichageFichier.jar $fic_conv &
		ancienPid=$!
	fi
	sleep 1
done