cursus.

Cours 1 · GénéralitésLeçon 2 sur 2

Prise en main du shell

4 h de lecture9 sections Version PDF

À la fin de cette leçon, vous saurez

Arborescence et commandes de base, redirections et tubes, variables d'environnement, premiers scripts.

En 1986, une revue demande à Donald Knuth d'illustrer la programmation lettrée. L'exercice : lire un texte, trouver les nn mots les plus fréquents, les afficher triés. Knuth rend un programme en Pascal d'une dizaine de pages, avec sa propre structure de données de hachage. Douglas McIlroy — l'inventeur des tubes Unix — est chargé de la critique. Il répond par six commandes :

tr -cs A-Za-z '\n' | tr A-Z a-z | sort | uniq -c | sort -rn | head -n 10

L'anecdote n'est pas là pour humilier Knuth, dont le propos était autre. Elle dit ce qu'est un système Unix : non pas un gros programme qui sait tout faire, mais une collection de petits outils qui font une chose, et un moyen de les brancher les uns aux autres.

Ce chapitre est placé avant toute théorie parce que les TP des quatre blocs suivants se font sous Linux. On ne programme pas un système dans lequel on ne sait pas se déplacer.

Une seule arborescence

Contrairement aux systèmes à lettres de lecteur, Unix n'a qu'un seul arbre, dont la racine est /. Un second disque, une clé USB, un partage réseau : tous apparaissent comme un répertoire quelque part dans cet arbre, opération appelée montage et étudiée au chapitre 7.

RépertoireContenu
/bin, /usr/binles exécutables
/etcla configuration, en fichiers texte
/home/nomle répertoire personnel d'un utilisateur
/tmples fichiers temporaires, effacés au redémarrage
/devles périphériques, vus comme des fichiers
/procl'état du noyau, vu comme des fichiers

Les deux dernières lignes méritent qu'on s'y arrête, parce qu'elles illustrent le principe « tout est fichier ». Le disque est accessible par /dev/sda, la carte son par /dev/snd, et lire /proc/cpuinfo affiche les caractéristiques du processeur — alors qu'aucun de ces fichiers n'existe sur un disque. Ce sont des vues, produites par le noyau au moment de la lecture. L'intérêt est immense : les mêmes commandes, les mêmes appels système et les mêmes droits s'appliquent aux fichiers réels et aux périphériques.

Un chemin absolu part de la racine ; un chemin relatif part du répertoire courant. Trois raccourcis servent constamment : . désigne le répertoire courant, .. son parent, ~ le répertoire personnel.

Les commandes qu'il faut avoir dans les doigts

CommandeRôle
pwd, cd, ls -loù suis-je, aller, lister avec les droits
cat, less, head, tailafficher en entier, page à page, le début, la fin
cp, mv, rm, mkdircopier, déplacer ou renommer, supprimer, créer
grep motif fichierne garder que les lignes contenant le motif
wc -lcompter les lignes
sort, uniq -ctrier, compter les doublons consécutifs
find . -name "*.c"chercher des fichiers dans une arborescence
ps, kill, toplister les processus, en tuer un, les observer
chmod, chownchanger droits et propriétaire (chapitre 7)
man commandele manuel, à consulter avant de chercher ailleurs

Deux remarques d'usage. rm ne demande pas confirmation et il n'existe pas de corbeille : la suppression est immédiate et définitive. Et uniq ne supprime que les doublons consécutifs, d'où le sort qui le précède toujours dans la ligne de McIlroy.

Redirections et tubes

C'est le cœur du chapitre, et il se comprend beaucoup mieux avec le chapitre 1 en tête.

Tout processus démarre avec trois descripteurs de fichier ouverts, désignés par un numéro — exactement les descripteurs que l'exercice du chapitre 1 validait :

0  entrée standard    (stdin)   par défaut : le clavier1  sortie standard    (stdout)  par défaut : l'écran2  sortie d'erreur    (stderr)  par défaut : l'écran aussi

Un programme bien écrit ne sait pas d'où viennent ses données ni où vont ses résultats : il lit 0 et écrit 1. C'est le shell qui décide à quoi ces numéros sont raccordés, avant de lancer le programme.

commande > fichier      la sortie va dans le fichier (écrase)commande >> fichier     la sortie s'ajoute à la fincommande < fichier      l'entrée vient du fichiercommande 2> erreurs     seules les erreurs vont dans le fichiercommande > tout 2>&1    les erreurs suivent la sortie

La séparation de 1 et de 2 n'est pas un détail : elle permet de rediriger un résultat dans un fichier tout en continuant à voir les messages d'erreur à l'écran. Un programme qui écrit ses erreurs sur la sortie standard les mélange aux données, et pollue le fichier produit.

Le tube (|) va plus loin : il branche la sortie standard d'une commande directement sur l'entrée standard de la suivante, sans fichier intermédiaire. Les deux processus s'exécutent en même temps, et le noyau régule leur allure — si le lecteur est plus lent, l'écrivain est suspendu jusqu'à ce que le tampon se vide. C'est déjà du producteur-consommateur, celui du chapitre 5, réalisé par le système.

D'où la relecture de la ligne de McIlroy :

tr -cs A-Za-z '\n'   remplace tout ce qui n'est pas une lettre par un saut de lignetr A-Z a-z           passe en minusculessort                 regroupe les mots identiques (uniq exige des doublons voisins)uniq -c              compte chaque groupesort -rn             trie par nombre décroissanthead -n 10           garde les dix premiers

Six outils qui ignorent tout les uns des autres, assemblés en une phrase.

Quiz · vérifiez votre compréhension Sans réponse

Quelle est la différence entre commande > sortie.txt et commande | autre ?

L'environnement, et comment une commande est trouvée

Chaque processus porte un ensemble de variables appelé son environnement, hérité de son parent. Le shell les affiche par env et en définit par export.

Les plus utiles sont HOME, le répertoire personnel, PWD, le répertoire courant, USER, LANG — et surtout PATH, qui mérite une explication détaillée parce qu'elle éclaire ce que fait le shell.

Quand vous tapez ls, le shell ne cherche pas partout. Il parcourt dans l'ordre les répertoires listés dans PATH, séparés par des deux-points, et prend le premier exécutable portant ce nom. D'où trois conséquences quotidiennes : une commande installée dans un répertoire absent de PATH reste introuvable alors que le fichier existe ; l'ordre décide laquelle de deux versions homonymes l'emporte ; et un programme du répertoire courant ne s'exécute que par ./programme, parce que . ne figure volontairement pas dans PATH — sans quoi un fichier nommé ls déposé dans un répertoire partagé s'exécuterait à la place de la vraie commande.

Une distinction revient sans cesse en TP : variable=valeur ne crée la variable que pour le shell courant, export variable=valeur la transmet aussi aux processus enfants. Le chapitre 3 expliquera pourquoi — l'environnement est copié au moment du fork, donc ce qui n'est pas exporté n'est pas dans la copie.

Premiers scripts

Un script shell est un fichier de commandes, avec quelques constructions de programmation.

#!/bin/bash# Compte les fichiers .c d'un répertoire donné en argument. repertoire=${1:-.}          # premier argument, ou "." s'il manque if [ ! -d "$repertoire" ]; then    echo "erreur : $repertoire n'est pas un répertoire" >&2    exit 1fi n=$(find "$repertoire" -name "*.c" | wc -l)echo "$n fichiers C dans $repertoire" for f in "$repertoire"/*.c; do    echo "  $(wc -l < "$f") lignes  $f"done

Quatre points à retenir de ce squelette. La première ligne, le shebang, dit au noyau quel interpréteur lancer — c'est lui qui permet d'exécuter le script directement. Les arguments s'appellent $1, $2, et $0 est le nom du script. Le code de retour compte : 0 pour succès, autre chose pour un échec, et c'est cette valeur que le shell teste dans un if ou après un &&. Enfin, les guillemets autour des variables ne sont pas facultatifs : sans eux, un nom de fichier contenant une espace est découpé en deux arguments, et c'est la source d'erreur numéro un des scripts débutants.

Quiz · vérifiez votre compréhension Sans réponse

Un étudiant compile un programme dans son répertoire courant, tape monprog et obtient « commande introuvable », alors que le fichier est bien là et exécutable. Pourquoi, et pourquoi ce comportement est-il volontaire ?

À vous

L'exercice reconstruit le tube. Vous disposez d'une poignée de commandes écrites comme des fonctions qui prennent un tableau de lignes et en rendent un autre — c'est exactement le contrat d'un outil Unix — et vous devez écrire l'évaluateur qui les enchaîne.

L'objectif final est de faire tourner la ligne de McIlroy sur un texte, et d'obtenir les mots les plus fréquents. Deux commandes manquent volontairement, uniq -c et sort -rn : ce sont celles qui font comprendre pourquoi sort doit précéder uniq.

Exercice · JavaScript · à vous de jouer

Écrivez uniq -c et sort -rn, puis faites tourner la ligne de McIlroy.

En attente
// Chaque commande : (lignes, arguments) -> lignes. C'est tout le contrat.
const COMMANDES = {
  // Découpe sur tout ce qui n'est pas une lettre : un mot par ligne.
  mots:   (l) => l.join(" ").split(/[^A-Za-zÀ-ÿ]+/).filter(Boolean),
  minus:  (l) => l.map((x) => x.toLowerCase()),
  sort:   (l) => [...l].sort(),
  head:   (l, n) => l.slice(0, Number(n)),
  grep:   (l, motif) => l.filter((x) => x.includes(motif)),
  wc:     (l) => [String(l.length)],

  // uniq -c : compte les répétitions CONSÉCUTIVES et préfixe le compte.
  uniqc:  (l) => {
    return l;   // ← à écrire
  },

  // sort -rn : trie par le nombre placé en tête de ligne, décroissant.
  sortrn: (l) => {
    return l;   // ← à écrire
  },
};

// L'évaluateur : "mots | minus | sort" appliqué à un texte.
function tube(entree, ligneDeCommande) {
  const etapes = ligneDeCommande.split("|").map((s) => s.trim().split(/\s+/));
  let flux = entree;
  for (const [nom, ...args] of etapes) {
    if (!COMMANDES[nom]) throw new Error("commande inconnue : " + nom);
    flux = COMMANDES[nom](flux, ...args);
  }
  return flux;
}

// ── À VOUS ────────────────────────────────────────────────────────────────
// 1. Écrivez uniqc et sortrn.
// 2. Expliquez pourquoi « mots | minus | uniqc » sans sort donne un résultat
//    faux, et vérifiez-le en lançant les deux versions.

const TEXTE = [
  "Le système gère la mémoire et le processeur",
  "le processeur exécute le programme que le système a chargé",
  "la mémoire contient le programme et ses données",
];

console.log("— sans sort —");
console.log(tube(TEXTE, "mots | minus | uniqc | sortrn | head 5").join("\n"));

console.log("— avec sort, la ligne de McIlroy —");
console.log(tube(TEXTE, "mots | minus | sort | uniqc | sortrn | head 5").join("\n"));

Console de sortie
Le résultat s'affiche dans la console

En travaux pratiques

Travaux pratiques 2 · sur machine

Une chaîne de traitement, sans écrire de programme

Traiter un vrai fichier de journaux uniquement en composant des commandes, et comprendre en quoi la redirection est une opération du système et non du langage.

3 h
Avant de commencer
  • Le TP 1
  • Un fichier de journaux d'au moins 100 000 lignes — le vôtre, ou un généré
  1. 1. Explorer sans tout lire

    Sans ouvrir le fichier dans un éditeur, donnez son nombre de lignes, ses dix premières, ses dix dernières, et sa taille en octets.

  2. 2. Les dix adresses les plus actives

    Écrivez une seule ligne de commandes qui extrait le premier champ, compte les occurrences et affiche les dix premières. Chronométrez-la.

  3. 3. Filtrer et compter

    Comptez les réponses en erreur, puis la part qu'elles représentent, puis leur répartition par heure. Une ligne par question.

  4. 4. Les trois flux

    Lancez une commande qui échoue sur la moitié de ses arguments. Redirigez la sortie normale vers un fichier et les erreurs vers un autre, puis les deux vers le même. Vérifiez l'ordre des lignes obtenues.

  5. 5. Un script réutilisable

    Transformez votre chaîne en script prenant le fichier en argument, avec une valeur par défaut, un contrôle des arguments et un code de retour correct.

  6. 6. Les droits

    Rendez le script exécutable pour vous seul. Testez-le sous un autre utilisateur. Puis expliquez pourquoi chmod 744 sur un RÉPERTOIRE ne fait pas ce qu'on croit.

  7. 7. Ce que le shell fait avant de lancer

    Créez des fichiers nommés a.txt, b.txt, puis lancez une commande avec l'argument étoile point txt entre guillemets et sans guillemets. Expliquez la différence avec echo.

C'est réussi quand
  • Votre chaîne des dix adresses tient sur une ligne et tourne en moins de deux secondes
  • Votre script renvoie un code non nul quand le fichier n'existe pas
  • Vous savez dire quel programme développe l'étoile, et quand

Ce que la suite en fait

Tout ce que le shell fait, il le fait par appels système, et le chapitre 3 va les nommer. Lancer une commande, c'est fork puis exec ; attendre sa fin, c'est wait ; le code de retour testé par un if est l'argument passé à exit. Le shell n'a aucun privilège particulier : c'est un programme ordinaire, et vous pourriez en écrire un.

Le tube, lui, revient deux fois. Au chapitre 3 comme moyen de communication entre processus, et au chapitre 5 comme cas d'école du producteur-consommateur — avec son tampon borné, son écrivain suspendu quand c'est plein et son lecteur suspendu quand c'est vide. Vous l'aurez utilisé cent fois avant de le programmer.

À retenir

Flashcards · 1 / 4Toucher pour retourner
Fin de la leçon

Vous avez parcouru les 9 sections.

Marquez-la terminée pour faire avancer votre parcours, ou revenez sur un point avant de passer à la suite.