Défis LinUEVE


La ligne de commande (ou le "shell"), est l'un des atouts des systèmes GNU/Linux. En effet, la qualité de cette interface est l'une des meilleure qui existe, et elle permet de faire simplement des tâches très utiles.

L'association LinUEVE propose régulièrement des défis destinés à ceux qui veulent apprendre à utiliser la ligne de commande.

Ces défis sont ouverts à tous ceux qui veulent y participer. Les membres de l'association LinUEVE et Liness seront tenus au courant de l'arrivée de nouveaux défi via leurs liste de diffusion.

 

Organisation des défis

Sur une période d'un mois, plusieurs défis sont proposés, avec différents niveaux de difficultés.

A la fin de la période, une réunion est organisée pour que tous les participants discutent de la qualité des solutions proposées, et voient les améliorations envisageables. Cet échange de connaissances devrait permettre à tout le monde de progresser rapidement.

Les participants peuvent contacter par e-mail l'organisateur du défi pour soumettre leur solutions, demander des éclaircissements ou des indices.

Défis de juillet 2011

Défi 1

Créez un script qui cherche une chaîne de caractères dans tous les fichiers d'un répertoire. Ce script prend deux arguments : le premier est le nom d'un répertoire, et le deuxième est la chaîne de caractères à trouver. Pour chaque occurence trouvée, le script doit afficher le nom du fichier, le numéro de la ligne, et la ligne elle-même.


Si vous avez réussi à faire cela, tentez d'améliorer votre script :

  • pour qu'il cherche aussi des occurences dans les sous-répertoires du répertoire fourni en argument
  • pour qu'il puisse chercher dans une liste de répertoires (stockée dans un fichier de configuration). Le nom du fichier de configuration sera passée en argument au script.
  • pour qu'il affiche le chemin absolu des fichiers dans lesquels il trouve une occurence

Défis du 18 juin 2011

Le premier défi débute le 27 mai 2011 et se terminera le 18 juin 2011. Vous avez donc jusqu'au 18 juin pour soumettre vos réponses.

Défi 0

Difficulté : facile (1/5).

Pour mener à bien ce défi, vous devez savoir naviguer dans le système de fichiers.

Pour créer un fichier vide, il faut taper :

touch NOM_FICHIER

Pour créer un nouveau répertoire, il faut taper :

mkdir NOM_REPERTOIRE

Pour remonter dans le répertoire parent, il faut taper :

cd ..


Ce défi consiste à faire les actions suivantes à l'aide de la ligne de commande :

  • Aller sur le Bureau (ou sur le Desktop)
  • Créer un répertoire nommé personnages_celebres
  • Aller dans le répertoire personnages_celebres
  • Créer deux répertoires : linus_torvalds et richard_stallman
  • Aller dans le répertoire linus_torvalds
  • Créer un nouveau fichier nommé naissance
  • Remonter dans le répertoire parent (personnages_celebres)
  • Aller dans le répertoire richard_stallman
  • Créer un nouveau fichier nommé naissance


Le résultat attendu est disponible dans cette archive : <bientôt disponible (quand les archives .zip seront autorisées sur le wiki)>

Défi 1

Difficulté : facile (1/5).

Ce défi consiste à afficher le nombre de fichiers qui se trouvent sur le bureau.

Par exemple, si vous avez trois documents et deux répertoire sur votre bureau, vous devez afficher "5".

Vous trouverez quelques explications pour mener à bien ce défi sur cette page.

Défi 2

Difficulté : intermédiaire (2/5).

Ce défi consiste à afficher le nombre de documents qui se trouvent sur le bureau.

Par exemple, si vous avez trois documents et deux répertoires sur votre bureau, vous devez afficher "3".

Vous trouverez quelques explications pour mener à bien ce défi sur cette page.

Défi 3

Difficulté : intermédiaire (3/5).

On considère le fichier d'exemple suivant, nommé liste.txt :

fichier1
fichier2
fichier3
repertoire/fichier4
repertoire/sous_repertoire/fichier5

Chaque ligne correspond à un nom de fichier qui n'existe pas encore. Vous devez créer ces fichiers de manière automatique avec un contenu vide. Certains fichiers se trouvent dans des répertoires qui n'existent pas non plus. Il faut aussi créer ces répertoires.


Une fois que votre script fonctionne, remplacez le contenu du fichier liste.txt par ceci :

Une_souris_verte
Qui courait dans l'herbe
Je l'attrape par la queue,/  Je la montre à ces messieurs  / Ces messieurs me disent :\
Je l'attrape par la queue,/  Je la montre à ces messieurs  /Trempez-la dans l'huile,
Je l'attrape par la queue,/  Je la montre à ces messieurs  /Trempez-la dans l"eau,
Je l'attrape par la queue,/  Je la montre à ces messieurs  /Ça fera un escargot
Un peu chaud; echo Tout chaud.
Pas très chaud"; echo Tout chaud.."
Vraiment pas très chaud'; echo Tout chaud...'
./Froi\d\" $(echo Tout chaud)....\"

Votre script devrait toujours fonctionner.

Le résultat attendu est disponible dans cette archive : http://dl.free.fr/rey033voa

Les outils que vous devez utiliser sont dans la liste des commandes intégrées au shell (section 14) et dans la liste des commandes externes au shell.

Indices

Essayez de trouver la solution avant de regarder les indices.


Indice 1 : vous devez savoir utiliser les boucles while

Indice 2 : la commande read permet de consommer une ligne de texte provenant de l'entrée standard.

Elle renvoie vrai si elle a pu lire quelque chose, elle renvoie faux s'il n'y a plus rien à lire. Ceci est très utile pour l'utiliser dans une boucle while, afin de consommer toutes les lignes provenant de l'entrée standard, en faisant une action sur chacune des lignes.

Vous devez connaître toutes les subtilités de la commande read.


Indice 3 : L'évaluation d'une ligne du shell se fait en sept étapes :

Il faut toujours se demander si l'une de ces sept étapes ne va pas venir mettre le bazar dans notre script. Les étapes les plus problématiques sont les trois dernières : découpage en morceaux (et donc perte des espaces multiples), remplacement des morceaux par des noms de fichiers, et suppression des caractères d'échappements. On peut interdire le découpage en morceaux en mettant la variable IFS="" . On peut interdire le remplacement des morceaux par des noms de fichiers avec set -f. On ne peut pas empêcher la suppression des caractères d'échappement, mais on peut les échapper eux-même en mettant un backslash devant (avec sed), ce qui préservera la ligne originale.


Indice 4 : Quand on peut, on évite toujours de soumettre nos données à l'évaluation du shell. Cela veut dire qu'on évite de stocker une donnée (ligne de texte étrangère) dans une variable, puis de substituer cette variable par son contenu dans une autre ligne, car à ce moment là il faut examiner les sept étapes pour voir si tout va bien se passer.

Une solution pour ne pas subir les substitutions intempestives est d'utiliser des commandes qui attendent des choses sur leur entrée standard, plutôt que des arguments classiques (qui nécessitent l'utilisation de variables). Et plutôt que de stocker le résultat de la commande dans une variable, on le passe directement à une autre commande avec un pipe (ou dans un fichier).

Exemple à éviter :

var1=$(cat fichier)
var2=$(echo $var1 | tr 'a' 'c')
var3=$(echo $var2 | grep 'c b')
echo $var3

Exemple qui évite les problèmes :

cat fichier | tr 'a' 'c' | grep 'c b'

À aucun moment, les shell n'est venu substituer quoi que ce soit aux lignes du fichier, donc on est tranquille.


Indice 5 : Il y a une deuxième façon de résoudre ce défi. Elle est un peu plus difficile à trouver à mon avis. Elle utilise la commande xargs.

Défi 4 : les années bissextiles

Difficulté : intermédiaire (2/5).

Les années bissextiles.

Réponses

Voici les réponses aux défis :

Première partie :

ls -A ~/Bureau  | wc -l


Deuxième partie :

ls -pA ~/Bureau | grep -cv /
ls -lA ~/Bureau | grep -c ^-
find ~/Bureau -maxdepth 1 -type f | wc -l


Troisième partie :

while read -r line; do
  mkdir -p "$(dirname $line)"
  touch "$line"
done < liste.txt
creerFichiers() {
  for fichier; do
    mkdir -p $(dirname "$fichier")
    touch "$fichier"
  done
}

xargs -a fichier -d '\n' creerFichiers
sed -e 's/\\/\\\\/g' -e "s/'/\\\'/g" -e 's/"/\\"/g' -e 's/~/\\~/g' -e 's/\$/\\$/g' -e 's/^/echo "/' -e 's/$/"/' fichier | sh


Quatrième partie :

est_bissextile() {
  [ $(("$1" % 4)) -eq 0 ] && { [ $(("$1" % 100)) -ne 0 ] || [ $(("$1" % 400)) -eq 0 ]; }
}

Documentation

Quelques liens pour l'utilisation de Bash (approximativement par ordre de difficulté):