Notes pour un cours d'introduction à Linux pour le CEPL
François-René Rideau

Ce cours présente les concepts de base du système Linux. Il ne requiert aucune connaissance préalable, mais s'adresse à des personnes ayant l'ambition de devenir réellement maître du sujet.

0 Remarques préliminaires
1 L'architecture des ordinateurs et de Linux
   1.1 Architecture de Von Neuman
   1.2 Mémoire
   1.3 Architecture virtualisée
   1.4 Persistence
   1.5 Multiplexage des ressources
   1.6 Appels systèmes
   1.7 Droits d'accès
   1.8 Niveaux d'abstraction
2 Le petit monde de Linux
   2.1 Le monde vu par un processus
   2.2 Conventions d'usage
      2.2.1 Paramètres de lancement d'une commande
      2.2.2 Options
      2.2.3 Configuration
      2.2.4 Gestion des erreurs
   2.3 L'arborescence des fichiers
      2.3.1 Noms de fichiers
      2.3.2 Liens durs et liens symboliques
      2.3.3 Droits d'accès des fichiers
      2.3.4 Organisation conventionnelle du système de fichiers
      2.3.5 Trouver des fichiers
   2.4 Les services en réseau
   2.5 L'interface graphique X
   2.6 Initialisation du système
3 Réflexes de base pour survivre
   3.1 Survivre avec l'interface graphique
   3.2 Savoir survivre avec la ligne de commande
   3.3 Utiliser l'aide
   3.4 Pour en savoir plus
   3.5 Apprendre à utiliser un éditeur
   3.6 Habitudes élémentaires de précaution
   3.7 Apprendre à programmer
      3.7.1 Programmation shell
      3.7.2 Programmation de scripts
      3.7.3 Langages de programmation sérieux
      3.7.4 Programmation système
      3.7.5 Programmation ennuyeuse
4 Le shell et ses amis
   4.1 Shell 101
      4.1.1 La notion de shell
      4.1.2 L'offre disponible
      4.1.3 Quel shell apprendre?
      4.1.4 Principe de base du shell
      4.1.5 Vos premières commandes
   4.2 Interaction avec le Shell
      4.2.1 Édition de ligne avec le shell
      4.2.2 Le shell en mode EMACS
      4.2.3 La complétion
      4.2.4 Le terminal sans éditeur
   4.3 Commandes simples
      4.3.1 Invocation d'une commande
      4.3.2 Commandes pour s'en sortir avec les fichiers
      4.3.3 Conventions d'appel pour les commandes
   4.4 Expansion des commandes par le shell
      4.4.1 Variables
      4.4.2 Échappements et guillemets
      4.4.3 Chemins d'accès
      4.4.4 Globbing
      4.4.5 Backquote
      4.4.6 Calculs arithmétiques
      4.4.7 Brace expansion
      4.4.8 Alias
   4.5 Commandes élaborées
      4.5.1 Redirection
      4.5.2 Regroupement de commandes
      4.5.3 Structure
      4.5.4 Fonctions et Scripts
      4.5.5 Programmation Shell Avancée
   4.6 Trucs à connaître
      4.6.1 Job control
      4.6.2 Noms de fichiers cachés
      4.6.3 Noms de fichiers ressemblant à des options
      4.6.4 Noms de fichiers difficiles à reproduire
   4.7 Configuration du shell
   4.8 Quelques amis du shell
      4.8.1 Petits utilitaires
      4.8.2 Contrôler la sortie
      4.8.3 Contrôler l'entrée
      4.8.4 Gérer la divergence des fichiers
      4.8.5 Comprimer et Archiver
      4.8.6 Vous connecter au réseau
      4.8.7 Aide à l'écriture
   4.9 Expressions régulières
5 Applications disponibles sous Linux
   5.1 Applicatifs les plus courants
   5.2 Quel type de documents produire?
   5.3 Correspondance rapide Windows vers Linux
   5.4 Correspondance rapide MacOS vers Linux
   5.5 Différences rapides entre BSD vers Linux
   5.6 Différences rapides entre Linux et les UNIX propriétaires
6 Tout se complique
   6.1 Disquettes, CD-ROMs, etc.
   6.2 Les bibliothèques à liaison dynamiques
   6.3 Gestion du temps
   6.4 Les terminaux
      6.4.1 Principe du terminal
      6.4.2 Configuration du terminal
      6.4.3 Multiplexer son terminal
      6.4.4 Programmer son terminal
      6.4.5 Déboguer son terminal
   6.5 Connexion à distance
   6.6 Administration
   6.7 Impression
   6.8 Compiler un programme
   6.9 Le noyau et ses modules
7 Quelques Exercices
   7.1 Exercice permanent
   7.2 Exercice d'administration de base
   7.3 Exercice de configuration graphique
   7.4 Copie d'écran
   7.5 Burn All GIFs!
   7.6 Pour les plus avancés...

0 Remarques préliminaires

Les notes de cours suivantes sont un document de travail en cours d'élaboration, à l'occasion des cours que j'ai commencé à donner pour le CEPL en octobre 2003. Vous pouvez suivre l'évolution de ces notes de cours sur mon serveur CVS. [Mon serveur CVS n'est plus à jour; j'utilise maintenant monotone.]

Les opinions professées sont les miennes et n'engagent que moi. En particulier, elles n'engagent ni le CEPL, ni mon employeur régulier, ni aucune association ni aucun groupe dont je ferais partie, et encore moins dont je ne ferais pas partie.

Ces notes de cours ne remplacent pas le cours et les travaux pratiques; elles ne se substituent pas non plus aux supports de cours de M. Pascal Bauler; elles constituent un complément théorique. Seule la pratique vous apprendra réellement Linux; ces notes n'ont pas d'autre objet que de vous permettre d'orienter votre pratique et d'en cristalliser plus vite les concepts fondamentaux.

Pour maîtriser Linux, vous devrez en connaître les principes fondamentaux, mais aussi savoir vous y retrouver dans la documentation. Ces notes de cours font systématiquement référence au manuel en-ligne de Linux, et contiennent aussi de nombreuses références à des pages web (notamment dans la section 3.4 "Pour en savoir plus"). Il y a aussi des notes de bas de page, qui se veulent des points de départ utiles à ceux d'entre vous qui voudront explorer un sujet particulier, mais dont la connaissance n'est pas indispensable à l'utilisation de Linux. Il est plus important de comprendre l'essentiel, et pour le reste de savoir ce qui existe et où chercher en cas de problème, que de maîtriser tous les détails de choses somme toute rarement utiles.

Pour ce qui est du mouvement des logiciels libres et de sa philosophie, qui soutendent tout le monde de Linux, voir mon article Le Libre Logiciel et surtout les liens sur lesquels il pointe (section "ressources").

1 L'architecture des ordinateurs et de Linux

Voici un court rappel sur l'architecture des ordinateurs en général, avec des notes sur Linux en particulier. Ce rappel permet aussi de mettre au clair certains concepts et le vocabulaire qui va avec.

1.1 Architecture de Von Neuman

Selon le modèle de machines formalisé par Von Neuman dans les années 1940 et toujours suivi (à quelques variantes près) par tous les ordinateurs à usage général (anglais: general-purpose computer) une machine est constituée d'un processeur (anglais: processor) et d'une mémoire (anglais: memory).

La machine effectue des calculs en exécutant séquentiellement instruction après instruction, des milliers de fois par seconde (de nos jours, en l'an 2003, parfois des milliards de fois).

Le processeur possède un nombre limité de registres (anglais: registers), pouvant chacun représenter un sous-ensemble fini des nombres entiers (de nos jours, presque toujours un nombre binaire sur 32 chiffres, pouvant donc représenter tout nombre positif inférieur à 2 puissance 32).

La mémoire possède un grand nombre de cases ayant chacune son adresse (anglais: address), un numéro qui peut être décrit dans un registre, pouvant chacune contenir un mot (anglais: word), qui peut être lu ou écrit depuis un registre.

Chaque instruction (anglais: instruction) pourra lire un petit nombre de registres ou de cases mémoires (typiquement moins de trois), et en modifier un petit nombre (typiquement une au plus). Chaque opération (anglais: operation) effectuée par chacune de ces instructions sur les registres et la mémoire est très simple: un exemple typique est l'addition de deux mots, le résultat étant stoqué dans un registre.

Les instructions sont elles-mêmes lues dans la mémoire, à l'adresse indiquée par un registre spécial appelé compteur du programme (anglais: program counter) ou pointeur d'instruction (anglais: instruction pointer); le pointeur d'instruction est incrémenté (anglais: incremented) (c'est-à-dire qu'on l'avance un peu) avant l'instruction suivante, à moins qu'il ne soit modifié par l'instruction en cours, alors appelée un saut (anglais: jump). Grâce à des tels sauts, le programme peut effectuer des boucles (anglais: loop) répétitives, de façon à effectuer un travail similaire un grand nombre de fois sur une grande quantité d'informations. Pour pouvoir effectuer des variantes à chaque fois, traiter des cas particulier, savoir arrêter le traitement et sortir d'une boucle, le processeur peut typiquement n'exécuter un saut ou une autre instruction qu'après avoir vérifié un test (anglais: test), qui consiste typiquement à déterminer si deux nombres étaient égaux ou différents.

Pour pouvoir communiquer avec le monde extérieur, l'ordinateur exécute des instructions spéciales dites d'entrée-sortie (anglais: input-output). Souvent (mais pas toujours), cette entrée-sortie prend la forme d'une opération de lecture ou d'écriture dans une case mémoire spéciale — on dit que l'entrée-sortie est mappée en mémoire (anglais: memory-mapped).

Pertinence: la façon dont se font les calculs, par un très grand nombre de petites modifications sur un espace mémoire (anglais: memory space), a une incidence sur la façon dont sont organisées les informations sur lesquelles on travaille, les données (anglais: data). Il est parfois utile de comprendre cette organisation pour agir sur le système (le corriger, l'améliorer). [1]

1.2 Mémoire

La mémoire de travail qui peut être lue ou modifiée à volonté est appelée mémoire vive (MEV) (anglais: random-access memory (RAM)); elle s'efface quand l'ordinateur est éteint ou remis à zéro. Un mémoire ne s'effaçant pas et ne pouvant pas (normalement) être modifiée, la mémoire morte (MEM) (anglais: read-only memory (ROM)) contient le programme de démarrage de l'ordinateur. Le reste de l'espace adressable (anglais: addressable space), correspondant aux numéros de cases mémoire qui ne sont pas occupés pas de la mémoire vive ni de la mémoire morte, ni des entrées-sorties mappées en mémoire, est inutilisé.

Dans les ordinateurs modernes, l'unité d'information traitée est le bit (chiffre binaire (anglais: binary digit)) (b), qui peut contenir un 0 ou un 1. les bits sont souvent groupés en mots de n bits, pouvant chacun représenter 2 puissance n combinaisons différentes, typiquement un nombre entier naturel entre 0 et (2 puissance n) - 1 ou un nombre entier relatif entre -(2 puissance (n-1)) et (2 puissance (n-1))-1. La taille d'un mot, quand elle n'est pas spécifiée, est celle d'un registre de la machine, qui est de nos jours en général 32 bits ou 64 bits.

Les mots de 8 bits sont appelés octet (anglais: byte) (1 B = 8 b), et peuvent représenter un nombre entier entre 0 et 255 (ou entre -128 et +127). Les textes sont composés de caractères (anglais: characters) où chaque caractère est en général stoqué sur un octet (parfois 2, parfois 4, parfois une valeur variable entre 1 et 4).

Les ordinateurs travaillent typiquement sur des mots de 32 bits (soit 4 octets, nombres entre 0 et 4.294.967.295, ou entre -2.147.483.648 et +2.147.483.647) ou pour les plus puissants 64 bits (soit 8 octets, nombres entre 0 et 18.446.744.073.709.551.615, ou entre -9.223.372.036.854.775.808 et +9.223.372.036.854.775.807).

La mémoire peut se compter en kibioctets (anglais: kibibytes) (1 KiB = 1024 B), mébioctets (anglais: mebibyte) (1 MiB = 1024 KiB = 1.048.576 B), gibioctets (anglais: gibibyte) (1 GiB = 1024 MiB = 1.073.741.824 B). Une page de texte prend typiquement entre 1 et 2 KiB; le texte de la bible tient entre 4 et 5 MiB. [2]

Un ordinateur est typiquement livré avec juste assez de mémoire vive pour faire tourner confortablement le système du jour. En 2003, le système du jour est Windows XP ou Mac OS X ou Linux avec une interface graphique KDE 3, et la bonne quantité de RAM est 256 MiB. Une utilisation allégée se satisfera de moins (128 MiB voire moins avec des efforts), une utilisation intensive nécessitera plus (512 MiB et davantage).

Pertinence: savoir combien de place prennent des données est important pour évaluer la taille et le prix des supports à acheter, des lignes sur lesquelles faire transiter ces données, etc. Comprendre le fonctionnement du système peut permettre d'éviter des gâchis coûteux (comme un plantage dû à un manque de ressources), en évitant des opérations inutiles (transfert de très gros fichiers sur un réseau lent qui sera saturé plutôt que sur un disque), ou en achetant à l'avance des ressources nécessaires (RAM, disque dur).

1.3 Architecture virtualisée

Un ordinateur moderne correctement programmé peut exécuter plusieurs programmes à la fois. On dit alors qu'il est multitâche (anglais: multitasking), chaque programme différent étant une tâche (anglais: task) [3].

Pour simplifier de nombreux problèmes de programmation, y compris la gestion du multitâche, les ordinateurs à usage général modernes permettent d'écrire des programmes comme si chaque programme s'exécutait sur son propre ordinateur rien qu'à lui, isolé des autres, une machine virtuelle (anglais: virtual machine). on dit que le processeur et la mémoire sont virtualisés (anglais: virtualized). Un tel ordinateur virtuel s'appelle un processus (anglais: process).

Un programme spécial, le noyau (anglais: kernel) sert à maintenir cette illusion. Il utilise un mode de fonctionnement spécial du processeur, où sont disponibles des instructions inaccessibles aux autres programmes; ce mode spécial est appelé mode noyau (anglais: kernel mode) par opposition au mode utilisateur (anglais: user mode).

Pertinence: les problèmes en mode noyau (par exemple, utilisation de matériels mal reconnus) peuvent complètement planter la machine. Les problèmes en mode utilisateur peuvent rendre certains processus non fonctionnels, pendant que les autres processus continueront à tourner; dans ce dernier cas, il sera souvent possible de relancer les calculs voire de récupérer les résultats intermédiaires des programmes plantés.

Note: sur les systèmes modernes comme Linux (et tous les Unix), Windows NT/2000/XP, ou MacOS X (qui fonctionne en fait avec un Unix), l'isolation entre les processus est bien assurée. Ce n'était pas le cas sur les systèmes Windows 95/98/ME, MacOS 9, et de nombreux systèmes plus ancien (MS-DOS, Windows 3, MacOS 6/7/8), où une erreur sur un processus normal pouvait planter tout le système.

1.4 Persistence

La mémoire d'un ordinateur est typiquement effacée à chaque fois que l'ordinateur est éteint ou plante. Pour que les données persistent (anglais: to persist) à travers les extinctions et les plantages, il faut effectuer des opérations d'entrées-sorties sur une mémoire persistente (typiquement un disque dur (anglais: hard disk)) [4]. On parle aussi de mémoire de masse (anglais: mass memory, mass storage) parce qu'elle peut typiquement contenir beaucoup plus de données que la mémoire vive.

Cette mémoire de masse est typiquement divisée en fichiers, pour que des processus indépendants puissent travailler sur des données indépendantes (fichiers différents) ou des données partagées (fichiers identiques). Typiquement, un fichier est vu comme une longue suite d'octets. Un fichier est identifié par son un nom qui est une chaîne de caractère (anglais: character string). Il peut aussi selon le système posséder des attributs (anglais: attributes) qui définissent typiquement la date de dernière modification, le nom de l'utilisateur qui possède le fichier, etc.

Les fichiers sont typiquement classés dans un hiérarchie de répertoires (anglais: directories). Un caractère spécial ('/' sous Linux et les autres Unix, '\' ou optionnellement '/' sous Windows ou MS-DOS, ':' sous les anciens Mac OS et l'interface utilisateur de Mac OS X) sert de délimiteur (anglais: delimiter) pour séparer dans le nom du fichier le nom de la suite de répertoires où il se trouve (anglais: dirname) du nom de base (anglais: basename) du fichier.

Pertinence: Les données qui persistent entre deux lancement d'un processus sont les données sur le disque. C'est sur ces données qu'il faut agir, pendant qu'aucun processus ne les utilise activement, pour corriger des fautes qui surgissent d'un lancement à l'autre. Ce sont aussi ces données qu'il faut surveiller et protéger par une sauvegarde (anglais: backup) régulière, pour éviter toute corruption par un processus erroné, un plantage matériel, ou autre événement catastrophique.

Note: Sous Windows, les fichiers peuvent être sur l'une de plusieurs unités de disque (anglais: disk units), comme indiqué par une lettre et deux points. Sous Linux, tous les fichiers sont visibles sous la même hiérarchie, et des hiérarchies de fichiers provenant d'autres partitions, d'autres disques, ou d'autres machines (via NFS) peuvent être montées (anglais: mounted) chacune sous un répertoire qui servira de point de montage (anglais: mounting point), grâce à la commande mount(8) (voir aussi la commande SUBST sous MS-DOS). Sous Linux, on peut explorer les fichiers disponibles avec le navigateur konqueror, ou avec des commandes comme cd, pwd, ls(1), cp(1), mv(1), ln(1) [5].

1.5 Multiplexage des ressources

Les programmes utilisateurs partagent le même processeur et la même mémoire, ayant chacun son tour une tranche de temps (anglais: time slice) durant laquelle il s'exécute pendant que les autres sont en attente. Ils partagent aussi la même mémoire de masse, chaque disque étant divisé en des grossières partitions (anglais: partitions), et chaque partition pouvant abriter un système de fichiers (anglais: filesystem), lui-même subdivisé en de nombreux fichiers et répertoires avec leurs attributs.

Le noyau assure que les ressources (anglais: resources) du matériel (anglais: hardware) sont ainsi disponibles pour chaque processus du système. On dit qu'il multiplexe (anglais: to multiplex) les ressources. [6]

Dans le cas où il n'y a pas assez de puissance processeur pour effectuer toutes les tâches avant que l'utilisateur s'en aperçoive, on sentira le système ralentir; d'éventuelles requêtes en provenances du réseau pourront ne pas être traitées. Solution: acheter un ordinateur plus puissant, ou être plus économe en puissance. Dans le cas où il n'y a pas assez de mémoire vive réelle, le noyau pourra utiliser une partie de la mémoire de masse comme une extension (très lente) de la mémoire vive virtuelle, grâce à des partitions ou des fichiers d'échange (anglais: swap) à configurer à l'avance ou au dernier moment.

Pertinence: grâce à ce multiplexage, il n'y a presque plus à se soucier des ressources individuelles de chaque processus, mais seulement des ressources globales du système. Il faut néanmoins parfois surveiller le niveau des ressources et comprendre quels processus occupent trop de ressources (avec les commandes free(1), top(1), ps(1), pidof(8), lsof(8), fuser(8)) et parfois tuer le processus gênant (avec les commandes kill(1), killall(1)). L'interface graphique ksysguard) permet de faire tout cela. Attention que chaque système de fichiers peut déborder indépendamment des autres, ce qui peut être un plus (si cela permet à certains services de survivre à un débordement qui en fait planter d'autres; typiquement, les utilisateurs sont sur une partition à part, sur un serveur les logs aussi, etc.), ou un moins (davantage de plantages potentiels à gérer).

1.6 Appels systèmes

Pour effectuer des entrées-sorties réelles (lecture ou écriture de fichier, communication avec l'utilisateur) ou virtuelles (communication avec d'autres processus), les processus doivent passer par le noyau; pour cela ils utilisent des séquences d'instructions spéciales constituant des appels systèmes (anglais: system call). Le noyau se charge des vraies entrées-sorties, et de la communication entre processus.

Les appels systèmes sont documentés dans la section 2 du manuel de l'utilisateur [7] [8] Des appels systèmes typiques sont read(2) et write(2), qui permettent de transmettre des données entre les entités gérées par le noyau et la mémoire du processus courant.

Pertinence: toutes les communications entre un processus et d'autres processus ou le monde extérieur passent par le noyau. Il est ainsi possible de voir ce qu'un processus fait, d'intercepter ses communications, etc., avec des commandes comme ps(1), lsof(8), strace(1), etc. C'est très utile pour diagnostiquer les erreurs. [9]

Note: Contrairement à d'autres systèmes, le noyau Linux ne se soucie que de faire un travail limité de manière fiable et robuste. Il est « bête et méchant »: les données qu'il manipule ne sont pour lui que des zéros et des uns sans signification particulière, en dehors des paramètres de ses appels systèmes en tant que tels. Charge aux processus de rendre les données intelligibles aux utilisateurs.

1.7 Droits d'accès

Le noyau gère un certain nombre d'utilisateurs [10].

Des utilisateurs spéciaux existent qui ont des droits d'administration sur l'ensemble du système ou sur certains sous-systèmes logiciels limités. Sous Linux, l'utilisateur dont l'UID est 0, traditionnellement appelé root, possède tous les droits d'administration. On dit aussi qu'il s'agit d'un super-utilisateur (anglais: super-user) [11]

Le noyau vérifie à chaque appel système que le processus appelant fait une opération correcte et que l'utilisateur qui a lancé le processus possède les droits d'accès (anglais: access rights) nécessaires pour l'opération indiquée.

Le système maintient une séparation stricte entre ses propres données et celles des processus. Il peut ainsi continuer à tourner et à effectuer ces vérifications sans être affecté par d'éventuelles erreurs ou tentatives de sabotage par les processus.

On peut inspecter les droits d'utilisateur du processus actuel avec la commande id(1), ou encore whoami(1) et groups(1), voire who am i.

Pertinence: il faut savoir gérer les droits d'accès, pour faire attention que les processus qui sont censés tournés ne voient pas leurs requêtes refusées par le noyau. Pour empêcher et détecter rapidement les erreurs, il est utile se séparer strictement les programmes avec des utilisateurs différents pour des services différents aussi bien que utilisateurs humains différents. Enfin, pour des raisons de sécurité, il faut veiller à ce qu'il n'y ait pas de trou de sécurité (anglais: security hole) qui permette à un utilisateur mal-intentionné voire à un intrus d'obtenir des droits d'accès qu'il ne devrait pas avoir.

Note: Pour les administrateurs avancés, Linux dispose

1.8 Niveaux d'abstraction

On peut concevoir le système à de multiples niveaux d'abstraction.

Le noyau parle au matériel (anglais: hardware), et gère des processus.

Chaque processus voit le monde que lui présente le noyau et exécute une activité propre. Pour interpréter les données de ce monde, il intègre des bibliothèques (anglais: libraries) qui gèrent des conventions de codage.

Certains processus tournent en tâche de fond et fournissent des services (anglais: services) (tel que l'affichage graphique, la connexion à distance, l'impression de documents). On les appelle des démons (anglais: daemons). Certaines bibliothèques gèrent implicitement l'appel aux démons adéquats.

Parfois, des processus normaux et des démons coopèrent pour faire émerger un comportement cohérent. On parle alors d'environnement (anglais: environment). (Exemple: l'environnement de bureau graphique KDE).

C'est la tâche de l'intégrateur (anglais: integrator) de faire en sorte que sa distribution (anglais: distribution) de logiciels forme un ensemble cohérent qui fournisse aux utilisateurs un environnement stable.

Pertinence: Si vous voulez modifier le système pour le réparer ou l'améliorer, il faut comprendre à quel niveau agir. À un niveau trop haut ou trop bas, le problème est trop abstrait ou trop complexe, les moyens de le décrire sont trop inadéquats, et les moyens d'agir trop chargés de détails pas assez précis.

2 Le petit monde de Linux

Comprendre Linux proprement dit, c'est le comprendre au niveau d'abstraction des processus, ce qu'un processus voit, qui lui est fourni d'une part par le noyau, et d'autre part par les conventions standards universellement respectées par tous les processus et présentes dans les bibliothèques standards, pour interagir via le système de fichiers et autres moyens de communication. Il est toujours temps ensuite de découvrir les niveaux d'abstraction supérieurs, fournis par divers services optionnels, par les outils de paquetage des distributions, par les environnement de bureau graphique, etc. Ces niveaux supérieurs ne pouvant pas tout contrôler et intercepter sur le système, ils doivent en fin de compte ne se fier principalement qu'aux invariants de cohérence offerts par ce niveau de base.

Notons qu'à ce niveau, Linux n'est qu'une variante d'Unix, tel que défini par la Single Unix Specification, qui remplace les normes et standards de fait précédents, comme POSIX, BSD et System V.

2.1 Le monde vu par un processus

Un processus possède de nombreux attributs, qui définissent sa vision du monde et la vision que les autres processus en ont. L'interface graphique ksysguard) (entre autres) permet d'inspecter visuellement la table des processus (anglais: process table). Les attributs d'un processus:

Quelques commandes pour inspecter les processus. On peut regarder les processus actifs avec top(1). On peut regarder l'état et autres propriétés d'un processus avec ps(1). On peut regarder les arbres des processus pères/fils avec pstree(1) ou ps x --forest. On peut regarder les appels systèmes d'un processus grâce à strace(1). On peut regarder les fichiers ouverts par un processus grâce à lsof(8). On peut regarder soi-même en allant dans /proc/$PID.

Pertinence: les attributs d'un processus définissent et limitent la façon dont il interagit avec le reste du monde, en bien comme en mal (erreurs). Le fait que la plupart de ces attributs sont hérités de processus père à fils limite aussi la façon dont les informations se propagent: il n'est pas habituellement possible d'échanger des droits d'accès, des descripteurs de fichiers, des variables d'environnement, etc., d'un processus à l'autre; pour se contacter, deux processus doivent donc ou bien faire partie d'un groupe de processus ayant un ancêtre commun qui les synchronise en leur fournissant les bons attributs hérités, ou bien utiliser le système de fichier ou les services réseaux, le client contactant le serveur via un chemin d'accès ou un port de communication bien identifié [23].

2.2 Conventions d'usage

2.2.1 Paramètres de lancement d'une commande

Quand on lance une nouvelle commande, on spécifie un certain nombre de paramètres explicites et implicites: le programme, la ligne de commande, les variables d'environnement sous présents explicitement dans l'appel système execve(2); les fichiers déjà ouverts et autres attributs et ressources sont hérités du processus père ou pas au moment de l'appel système fork(2) ou sa variante clone(2) précédent le exec. Lors de l'execve(2), la mémoire est remise à zéro (aucun mapping), puis de nouveaux mappings sont créés pour le programme, les données privées du processus et sa pile d'exécution. Le programme peut alors faire de nouveaux mappings (pour les bibliothèques partagées notamment, ou pour demander plus de mémoire), et lire ou mapper des fichiers de configuration, des fichiers de travail, etc.

Quand on lance un nouveau programme en exécutant les appels systèmes [24], le noyau va donc charger le programme indiqué et lui fournir:

2.2.2 Options

Le comportement des commandes peut souvent être modifié par un nombre plus ou moins grand d'options.

Par convention, ces options sont spécifiés parmi les arguments d'appel, et reconnus en ce qu'ils commencent par le caractère - [27] De nombreux programmes ont des options courtes, introduites par un seul caractère -, et nommées par un caractère, et que l'on peut regrouper: ainsi ls -as active les options -a et -s. Parfois, il existe aussi des options longues, introduites par deux caractères -, et nommées par un mot; ainsi la commande précédente est équivalente à ls --all --size. D'autres programmes n'acceptent que des options longues avec un seul -.

Le caractère - tout seul signifie souvent l'utilisation de l'entrée ou de la sortie standard (selon le contexte) comme fichier sur lequel la commande doit agir. Doublé -- il signifie souvent la fin des options du programme appelé, le reste étant des paramètres normaux pour ce programme, même s'ils commencent par - [28].

2.2.3 Configuration

Pour déterminer ce qu'elle va faire effectivement, une commande regardera sa configuration, par ordre de priorité décroissante:

ls(1) et grep(1) sont vos amis pour identifier les fichiers de configuration, ainsi que strings(1) sur le binaire ou strace(1), mais bien sûr commencez par man(1), et s'il n'est pas concluant, tentez rpm -qil ou dpkg -L.

2.2.4 Gestion des erreurs

La convention est qu'un processus écrira sur sa sortie standard d'erreur (descripteur de fichier numéro 2) des textes relatifs aux erreurs ou événements anormaux ayant lieu. Les démons (processus tournant en tâche de fond) pourront souvent utiliser la facilité syslog(3) (apropos syslog) pour le journal des événements anormaux rencontrés. La configuration par défaut de syslog va en général correspondre à stoquer des journaux des événements survenus dans /var/log, mais il est possible de filtrer ce journal et de le rediriger vers d'autres fichiers, voire une autre machine, etc.

En se terminant, chaque processus donne un code d'erreur, nombre compris entre 0 et 255. La convention est que le 0 indique que le programme s'est déroulé correctement, tandis qu'autre chose indique une erreur qui n'a pas pu être corrigée.

Dans un shell, le code d'erreur du dernier programme est disponible dans la variable $?.

2.3 L'arborescence des fichiers

Les fichiers sont organisés dans une structure de répertoires (anglais: directories) récursivement imbriqués les uns dans les autres, appelée système de fichier (anglais: filesystem). On parle aussi d'arborescence de fichiers ou de hiérarchie de fichiers.

2.3.1 Noms de fichiers

Le nom d'un fichier (anglais: filename) proprement dit est une chaîne de caractère ne comprenant pas ni le caractère nul (de code 0), ni le caractère barre de division (anglais: slash) '/'. Sur le système par fichier standard de Linux (ext2 ou ext3), les noms de fichiers sont limités à 255 caractères.

Sous Unix, traditionnellement, la différence entre minuscule et majuscule compte. Ceci vaut pour les noms de fichiers et donc aussi par conséquence pour les noms de programmes exécutables (qui sont des fichiers) [30]

Un chemin d'accès à un fichier (anglais: pathname) (ou nom de fichier) est une chaîne de caractère formé de noms de fichiers séparés par des slashes '/'. Le fichier spécifié est obtenu en entrant successivement dans chacun des répertoires nommé par un composant du chemin d'accès jusqu'à arriver au dernier composant, qui est le nom du fichier désigné.

Si le chemin d'accès commence par un slash, alors il est dit être absolu (anglais: absolute), et commence à partir du répertoire racine. Sinon, il est dit être relatif, et commence à partir du répertoire de travail courant du processus actuel.

Au cours de la traversée d'un chemin d'accès, les noms de fichiers '.' et '..' font toujours référence au répertoire actuel et au répertoire père du répertoire actuel [31]

2.3.2 Liens durs et liens symboliques

D'autres fichiers sont des liens durs (anglais: hard links, hardlinks), de façon que plusieurs noms de fichiers correspondent à un même fichier réel, avec le même contenu et les mêmes attributs. Toute modification du fichier via un de ses noms sera visible sous chacun de ses autres noms, jusqu'à ce que le liens soit éventuellement brisé. Les fichiers de même lien dur sont identifiés par leur numéro d'inode, visible avec ls -li [32] Un fichier ne disparaît vraiment du disque que lorsque le dernier lien est supprimé. On ne peut faire de lien dur que vers un fichier de la même partition.

On peut lire le nombre de liens durs vers un fichier avec la commande ls -l. Notez qu'un répertoire a toujours de nombreux liens, au moins deux, puisque chaque parent est lié à son fils et réciproquement. On ne peut toutefois pas faire de lien dur sur un répertoire autrement que par une telle relation père-fils [33].

Il existe une autre sorte de liens. Certains fichiers sont des liens symboliques (anglais: symbolic links, symlinks) qui pointent vers un chemin d'accès (absolu ou relatif). Quand le noyau voit un tel lien, il remplace toute tentative d'accès à ce lien par une tentative d'accès au chemin désigné [34]

Les liens symboliques servent très souvent, pour préserver des chemin d'accès connus aux fichiers malgré le fait que l'arborescence réelle a été déplacée [35]. Ainsi, les liens symboliques permettent de conserver des chemins d'accès logique (anglais: logical) à chaque fichier.

Les liens durs, eux, servent à partager des données devant être accessible depuis un nombre indéterminé d'entrées de répertoire. De tels fichiers partagés sont souvent gardés en lecture seule après leur création initiale. Un exemple connu d'utilisation de liens durs est pour la gestion de groupes de discussion avec multipostage comme USENET (voir le serveur innd). Une autre application utile est pour le développement de versions concurrentes d'un même logiciel: on pourra ainsi partager les fichiers sources qui n'ont pas changé entre les multiples arborescences d'un logiciel dont on maintient simultanément plusieurs versions (dernière version stable connue, nouvelle version stable en provenance d'amont, version de développement en amont, version localement modifiée, etc.) [36].

2.3.3 Droits d'accès des fichiers

Chaque fichier a des droits d'accès déterminant qui a le droit d'effectuer quelles opérations sur le fichier. chgrp(1). root peut utiliser chown(1).

chmod(1)

rwx setuid setgid sticky [37]

Droits en octal.

Truc: rendre répertoires et exécutables accessibles à tous sans changer les autres droits: chmod -R a+X foo

Un paramètre spécial du processus actuel, disponible via la commande shell interne umask (qui utilise l'appel système umask(2)), détermine les droits d'accès qui seront refusés sur les fichiers créés. Le umask est noté avec la syntaxe numérique octale du chmod. Traditionnellement, le umask est mis à 022 pour que seul l'utilisateur actuel puisse modifier ses propres fichiers. Il peut être mis à 002 si le groupe actuel ne contient que l'utilisateur lui-même et des personnes de confiance sur le projet dont on modifie les fichiers. Il pourra être mis à 077 pour être paranoïaque et peu partageux vis-à-vis des autres utilisateurs. etc.

Par delà les droits d'accès unix traditionnels tels que décrits ci-dessus, certains systèmes de fichiers fournissent des droits supplémentaires. Par exemple, le système de fichier ext2 ou ext3 de Linux offre des attributs accessibles avec les commandes lsattr(1) et chattr(1).

Traditionnellement, quand on crée un utilisateur normal, on lui crée un répertoire personnel ou répertoire maison (anglais: home directory), (normalement répertorié dans le fichier passwd(5). Ce répertoire appartiendra au dit utilisateur, et il pourra ainsi y faire ce qu'il veut, sans être embêté par d'autres utilisateurs. Par convention, le répertoire actuellement considéré comme maison et connu via la variable d'environnement HOME.

2.3.4 Organisation conventionnelle du système de fichiers

Grâce aux techniques respectives des points de montage [38] et des liens symboliques [39], l'organisation du système de fichier (anglais: filesystem layout) est logique (anglais: logical) plutôt que physique (anglais: physical). C'est-à-dire que ce qui compte pour déterminer le chemin d'accès à un fichier est la structure logique de l'intention dans laquelle existe le fichier, plutôt que la structure physique du disque sur lequel est stoqué le fichier.

Pour le standard, plus ou moins respecté par toutes les distributions modernes, voir le FHS.

Fichiers systèmes: les répertoires /bin, /lib, /sbin, /usr, contiennent des fichiers des programmes gérés par votre distribution; le système ne modifie pas ces fichiers en temps normal, sauf quand vous installez de nouveaux paquets. /usr est lui-même divisé en de nombreux répertoires et sous-répertoires.

Fichiers de travail: Le répertoire /var contient les fichiers de travail persistents des services installés. Le répertoire /tmp (et/ou parfois aussi /var/tmp) contient les fichiers temporaires du système, qui n'ont pas besoin de survivre à un redémarrage des programmes ou services qui les utilisent.

Fichiers de configuration du système: sous Unix, les fichiers de configuration sont généralement dans /etc, et les distributions modernes s'y tiennent assez bien pour les paquets qu'elles gèrent. Si vous installez des logiciels d'une autre provenance, vous pourrez aussi en avoir dans /opt/etc, /usr/local/etc, /var/qmail, etc.

Fichiers magiques: Parmi les ressources multiplexées par le système, il y a les périphériques (anglais: devices) connectés au système (écran, clavier, souris, son, réseau, disque, imprimante, scanner, etc.), chacun géré par un gestionnaire de périphérique (anglais: device driver) approprié. Sous Linux, ces périphériques sont souvent vus comme des fichiers spéciaux, « magiques », dans le répertoire /dev. D'autres fichiers « magiques » pour configurer le système sont dans /proc (allez donc y faire un tour avec cd, ls et cat) voire dans certaines versions du système dans /sys. Seul l'administrateur a normalement le droit d'agir directement sur ces fichiers spéciaux.

Fichiers utilisateurs: /root contient souvent le répertoire utilisateur de root, tandis que les autres utilisateurs sont souvent mis dans /home (en général le répertoire par défaut où la distribution les met; certains préfèrent /users ou quand il y a beaucoup d'utilisateurs, une arborescence logique en-dessous de /home ou /users). Le répertoire utilisateur de l'utilisateur courant est dans la variable d'environnement HOME, que les shells modernes abrègent souvent en ~. Les fichiers de configuration personnels de chaque utilisateur sont dans son répertoire personnel avec un nom commençant par un point (par exemple ~/.bashrc pour le programme bash(1)).

Autres: /opt ou /usr/local contiennent typiquement des programmes qui ne sont pas gérés par la distribution, mais par un autre système de paquetage, voire installés manuellement par l'administrateur. /mnt est souvent utilisé pour des montages temporaires. Pour le reste, votre imagination est la seule limite!

2.3.5 Trouver des fichiers

La documentation peut aider à les trouver.

2.4 Les services en réseau

Ce qui a fait le succès d'UNIX en général et de Linux en particulier a toujours été leur intégration aux réseaux utilisant les protocoles de l'Internet (TCP/IP).

Linux gère aussi d'autres types de réseau et de services (par exemple, AppleTalk grâce au logiciel netatalk(8) ou Novell grâce à marsnwe(8)), mais ceux qui valent le plus la peine d'être connus sont les réseaux IP. [41]

À introduire dans cette section:

Pour une discussion de ce qui fait qu'UNIX est historiquement particulièrement adapté aux services en réseau, voir la discussion sur la notion de terminal.

2.5 L'interface graphique X

Architecture en couches: serveur X, window manager, desktop environment (GNOME, KDE, XFCE), bibliothèques de fonctions, applications, etc.

transparence réseau (anglais: network transparency): les programmes sont censés pouvoir s'exécuter de n'importe quelle machine vers n'importe quelle machine.

export DISPLAY=:0
export DISPLAY=machine:0

Authentification: xhost(1x), xauth(1x). Sur certaines distributions, la configuration par défaut du serveur X n'écoute pas sur le réseau, et vous devez modifier ce point-là à la main.

Pour davantage de sécurité, tunnel ssh ou IPSEC, etc.

De nombreuses complications font que la transparence réseau marche mal dès qu'on veut faire plus que d'afficher des données ou que l'on veut utiliser l'une de diverses extensions au protocole X, voire des protocoles qui agissent à côté de X: le son, l'accès direct à l'écran, les fontes, etc., sont chacun source de problèmes de configuration "intéressants".

Un résumé de l'état de l'art: Open Source Desktop Technology Road Map.

2.6 Initialisation du système

Finalement pour comprendre Linux, il faut comprendre comment Linux démarre. Pour tous les détails, lire le HOWTO adéquat...

Démarrage de la machine: la ROM initialise le matériel, puis cherche un "secteur d'amorce" sur une disquette, un disque dur, le réseau, etc.

Le secteur d'amorce va charger le reste du programme d'amorce du système. Pour Linux, il s'agit typiquement de GRUB ou de LILO, qu'il ira chercher à tel numéro sur tel disque, tel que spécifié lors de l'installation de GRUB ou de LILO.

Le programme d'amorce offre éventuellement un menu, et en fin de compte, décide de charger tel noyau avec tel paramètre, à moins qu'il ne donne la main au programme d'amorce d'un autre système (par exemple, au secteur d'amorce d'un disque contenant Windows).

Le programme d'amorce cherche le noyau à l'endroit indiqué. GRUB lit le système de fichier et retrouve le noyau d'après son nom de fichier; LILO est plus bas niveau et reconstitue le noyau en cherchant tel et tel secteur du disque; LILO doit donc être réinstallé à chaque fois que le noyau est modifié.

Une fois chargé par le programme d'amorce, le noyau prend la main. Le programme d'amorce peut donner au noyau des paramètres de lancement ainsi qu'un "disque de démarrage" virtuel à charger en mémoire contenant des programmes pour démarrer le système.

Le noyau lance le processus init(8) sur son disque de démarrage. S'il s'agit d'un disque de démarrage virtuel, il va généralement donner la main au programme init du disque réel après avoir chargé les gestionnaires de périphériques adéquats.

Pour la suite, voir la documentation de init(8), sa vie son œuvre, et ses fichiers de démarrage.

La configuration de démarrage selon la convention System V, et ses runlevels.

3 Réflexes de base pour survivre
3.1 Survivre avec l'interface graphique

C'est difficile à décrire seulement qu'avec des mots. Suivez le cours.

Le processus qui parle au matériel: le serveur X. Il accepte des connexions de clients en local ou sur le réseau, qui communiquent en utilisant le protocole X11R6 avec diverses extensions [42]. Lancer le serveur X quand il n'est pas lancé automatiquement: startx [43].

Attention:

3.2 Savoir survivre avec la ligne de commande

Pour lire le manuel d'une commande nommée toto: man toto. Autres façons d'obtenir de l'aide (parfois): toto -h ou toto -\? ou toto --help ou toto -help ou simplement toto.

Si toto est un programme interactif, essayer au cours du programme la commande help ou les touches: h ? H Ctrl-H Alt-H.

Pour sortir d'une commande, essayer: q Q Ctrl-Q Ctrl-D

Pour interrompre une commande, essayer: Ctrl-C Ctrl-\ Ctrl-Z.

Votre shell est sans doute bash(1) (mais je recommande de changer pour zsh(1) si vous le pouvez, en utilisant chsh(8)), donc pour avoir de l'aide à propos du shell: man bash.

Pour lire son courrier électronique: mutt

Pour avoir la liste des fichiers: ls

Pour connaître l'effet d'une commande: type commande

Pour lister le contenu d'un fichier: less fichier

Pour changer de répertoire: cd

Pour quitter le shell: exit ou parfois logout ou C-d

Autres commandes courantes: zsh cp mv ln rm mkdir rmdir echo cat gzip tar grep cut ps

3.3 Utiliser l'aide

Une distribution Linux est vient typiquement avec une documentation en-ligne extensive, sans parler des sites sur internet (voir la section "pour en savoir plus"). Il faut juste savoir où et comment regarder.

Sous KDE, ouvrez le programme khelpcenter, et naviguez.

Avec la ligne de commande, vous disposez de la commande man(1) pour lire les pages de manuel, de la commande info(1) (ou de emacs(1)) pour lire les pages info, de lynx(1) (ou links(1) ou w3m(1)) pour lire les fichiers html, de la commande less(1) pour lire les fichiers textes, ou zless(1) s'ils sont comprimés avec gzip(1). Les fichiers dvi peuvent être lus avec xdvi(1) (ou advi(1) s'ils sont actifs); les fichiers ps ou pdf peuvent être lus avec gv(1x), kghostview(1) ou ggv(1); les fichiers pdf peuvent aussi être lus avec xpdf(1) ou acroread(1) (logiciel exclusif).

Les pages de manuel sont dans /usr/man et dans les autres répertoires de votre $MANPATH, mais vous pouvez les consulter directement avec un nom de commande, ou chercher un mot dans l'index avec man -k motclef. Les pages info(1) sont dans /usr/info et dans votre $INFOPATH. De nombreux fichiers de documentation en mode texte ou en html se trouvent selon la distribution dans /usr/doc ou /usr/share/doc; c'est là, dans un répertoire HOWTO qu'on trouve souvent les notables documents du Linux Documentation Projet qui expliquent comment résoudre divers problèmes ciblés quand on débute avec Linux, (et en fait même quand on connaît déjà Linux mais qu'on débute avec le domaine du problème en question).

3.4 Pour en savoir plus

Les pages de manuel (disponibles graphiquement avec khelpcenter, ou par la ligne de commande avec man(1)) sont une excellente référence, mais ne sont pas orientées vers la résolution de problèmes spécifiques. Heureusement, il existe de très nombreux documents sur Internet écrits par des techniciens ayant déjà affronté les mêmes problèmes que vous, et qui vous permettront de survivre.

3.5 Apprendre à utiliser un éditeur

Si vous devenez un utilisateur expérimenté, vous devrez apprendre à utiliser un éditeur de fichiers textes.

Parmi les plus simples, il y a en mode graphique l'éditeur kwrite qui suffit pour les besoins de tous les jours.

L'éditeur visuel le plus universellement disponible sous tous les systèmes UNIX est vi(1); pour pouvoir se sortir de cas extrêmes où on démarre sur une disquette ou un système autrement restreint, il est donc utile de savoir survivre sous vi. Toutefois, en-dehors de cette utilisation, vi lui-même est à éviter. Il existe par contre une version très améliorée et extensible de vi: vim(1) (y compris la version intégrée à KDE, kvim(1)), qui est un éditeur tout à fait décent; je conseille ceux qui veulent apprendre vi à lire le tutoriel de vim et à se faire une cheat sheet (demander à Google).

La mère de tous les éditeurs, véritable système d'exploitation à lui tout seul, écrit et extensible dans son propre dialecte de Lisp, c'est GNU EMACS. Emacs se décline dans sa version officielle de la FSF, ou dans la version rivale XEmacs. Dotés de nombreux modules pour faciliter le développement de programmes dans de très nombreux langages, ou pour lire son courrier, naviguer sur le web, se connecter à des bases de données, etc. Là encore, commencez par utiliser le tutoriel et par imprimer la reference card. Vous trouverez aussi dans ces notes de cours, à la section sur le shell en mode EMACS, les quelques commandes les plus usuelles.

Notez que vim, emacs et xemacs sont tous trois disponibles sous Windows comme sous MacOS X, et que les talents que vous acquerrez à les maîtriser pourront donc être aussi mis à profit sur vos autres plateformes de travail.

3.6 Habitudes élémentaires de précaution

Faites des sauvegardes régulières avec copie distante. Par exemple, j'utilise rsync(1) avec une clef SSH (créée par ssh-keygen(1)).

Quand vous changez un fichier de configuration important, dont le dysfonctionnement pourrait vous empêcher de plus vous connecter, gardez ouverte une console avec un shell tournant sous l'ancienne configuration jusqu'à avoir vérifié que la nouvelle configuration tourne. De même, ne changez rien au démarrage de votre machine sans avoir une disquette ou un CD de démarrage à votre disposition. Effectuez vos changements majeurs de distribution sous chroot(8) et ne redémarrez qu'après avoir bien testé.

Ai-je bien dit qu'il fallait faire des sauvegardes régulières avec copie distante?

Mettez vos fichiers de configuration importants sur un serveur CVS ou équivalent pour pouvoir récupérer les anciennes versions qui marchaient et consulter les différences qui auront déclenché un dysfonctionnement. Je ne suis pas tout à fait au courant de l'offre, mais on me dit qu'il y a mieux de nos jours: j'utilise encore CVS, mais de nos jours, Subversion fait tout ce que fait CVS en mieux et plus robuste et de façon quasi-compatible, et il existe de nombreux logiciels qui proposent des approches plus intéressantes, dont Arch, Code*ville, DARCS, Meta-CVS, monotone, OpenCM. Affaire à suivre.

Au cas où je n'aurais pas été assez clair, faites des sauvegardes régulières avec copie distante.

Oh, j'allais oublier la règle la plus importante: faites des sauvegardes régulières avec copie distante.

3.7 Apprendre à programmer

La différence entre un programmeur et un utilisateur, c'est que le programmeur sait qu'il n'y a pas de différence entre programmation et utilisation. Vous apprendrez donc à programmer, que vous en soyiez conscients ou pas. Si vous devenez utilisateur expérimenté, autant vous y mettre consciemment. Quelques soient les langages que vous apprendrez, le meilleur moyen, après avoir parcouru des documents introductifs, est lire des exemples simples en ayant le manuel sous la main. Repérez donc des programmes simples utilisant le langage que vous apprenez, et lisez-les, exécutez-les pas-à-pas, etc.

Sur l'esprit général dans lequel le code linux existant est écrit, voir par exemple TAOUP; cela dit, il y a d'autres approches ou d'autres vues sur la programmation, et si la programmation est un sujet qui vous passionne, vous trouverez plus et mieux sur des sites adéquats.

3.7.1 Programmation shell

Pour survivre dans la vie de tous les jours, il faut connaître un minimum votre shell: bash, ou celui que je recommande, zsh. Si vous administrez une machine, vous aurez souvent besoin d'écrire des petits shell scripts, ou du moins de modifier des scripts existant pour les adapter à vos besoins.

Pour écrire des shells script portables, il faut aussi savoir se limiter aux fonctionnalités du shell POSIX de base (soupir!). Mais pour vous-mêmes, n'hésitez pas à utiliser toute la puissance de votre shell; quoique si vous avez vraiment besoin de puissance, choisissez un meilleur langage ci-dessous.

Vous voudrez aussi sans doute configurer votre shell pour le rendre plus convivial; outre la documentation de votre shell (sur votre machine ou le site officiel) et les fichiers d'exemples qui vont avec, il y a des sites comme d'information comme shelldorado [44].

3.7.2 Programmation de scripts

En administrant des machines, vous voudrez souvent écrire des petits programmes, des scripts, qui automatiseront vos tâches, lieront ensembles les programmes disparates que vous utiliserez, etc. Plusieurs solutions s'offrent à vous:

3.7.3 Langages de programmation sérieux

Dès que vous commencez à écrire un programme sérieux (disons, plus de cinq mille lignes), alors fuyez les langages de scripts, et optez pour un vrai langage de programmation, conçu pour faire de la programmation propre, efficace, bien structurée, robuste, expressive, etc. Voici les langages les plus adaptés à la programmation sérieuse, qui offrent chacun à sa façon les paradigmes de programmation fonctionnel, objet et générique, avec en plus des spécifités propres:

Si vous vous tournez vers la programmation, il existe aussi de nombreux autres langages chacun avec ses dialectes, qui pourront vous ouvrir l'esprit vers de nouveaux horizons tout en vous permettant de faire de la programmation sérieuse: Haskell, Scheme, Mercury, Oz, etc. Il y a de très nombreux langages de programmation. Voir par exemple la page Programming Languages du wiki de TUNES.

3.7.4 Programmation système

Parfois, vous aurez besoin de descendre dans des couches relativement basses du système, ou du moins de comprendre ce qui s'y passe. Pour cela, il vous faudra apprendre un langage d'implantation système, même si dans la pratique, il est recommandé de ne s'en servir qu'avec parcimonie, et d'enrober les routines et bibliothèques écrites dans ces langages pour utilisation dans un langage sérieux de haut niveau comme vu précédemment.

Si ces langages systèmes, de bas niveau, permettent d'obtenir des performances supérieures dans des micro-benchmarks, dans des programmes de taille raisonnable, les langages de haut niveau les rattrapent en permettant d'optimiser plus facilement l'organisation des structures de données; de plus, le cas échéant, il est toujours possible d'employer après profilage (anglais: profiling) (voir man -k prof) un langage de bas niveau juste pour les quelques boucles fines (anglais: tight loops) qui occupent la majeure partie du temps de vos programmes.

3.7.5 Programmation ennuyeuse

Il est des langages de programmation qui offrent de mauvais compromis techniques, mais qui sont néanmoins répandus, pour des raisons économico-politiques. Ils sont aussi disponibles sous Linux.

4 Le shell et ses amis
4.1 Shell 101

4.1.1 La notion de shell

Dans la terminologie UNIX, un shell est un logiciel qui offre à l'utilisateur une interface vers l'ensemble du système. Littéralement, le mot signifie coquille, et indique bien que c'est le composant le plus extérieur du système, directement en contact avec l'utilisateur, par opposition au noyau qui est au cœur du système et aux applications entre les deux.

Si on peut considérer que l'interface graphique GNOME ou KDE constitue un shell, et si l'éditeur EMACS est effectivement utilisé par certains comme un shell dans tous les sens du termes, on réserve l'appellation de shell au sens strict à des programmes conçus pour fonctionner dans un terminal en interprétant les commandes de l'utilisateur ligne-à-ligne, et en lançant les programmes spécifiés.

Le shell est le sel et la terre de l'utilisateur UNIX expérimenté:

Sur le sujet de ligne de commande par opposition à l'interface graphique, voir l'article célèbre de Neal Stephenson In the beginning was the command line (aussi ici ou voire sur le site original).

4.1.2 L'offre disponible

Il existe de nombreux shells: ash(1), bash(1), csh(1), dash(1), posh(1), ksh(1), pdksh(1), rc(1), tcsh(1), zsh(1), sans parler de shells spéciaux pour systèmes de secours comme busybox(1), kiss(1) ou sash(1), variantes que je ne traiterai pas davantage ici [45].

Le standard POSIX, repris par la norme SUS, définit ce que sait faire le shell standard de base /bin/sh. La plupart des shells reprennent ce standard et l'étendent avec des fonctionnalités spécifiques. GNU bash(1) est le premier parmi ceux-là, et à l'époque où tous les marchands d'UNIX exclusifs livraient des shells antiques et bogués jusqu'à la moëlle, il s'est imposé comme standard de fait chez les utilisateurs expérimentés; il est aujourd'hui le plus répandu dans le monde Linux, et un standard officiel du Linux Standard Base. Aussi, dans la plupart des distributions, /bin/sh est un lien symbolique ou dur vers /bin/bash (qui détecte qu'il a été appelé comme sh et adapte son comportement en correspondance pour être le plus compatible possible). Cela au point que parfois, des programmes censés marcher sur n'importe quel UNIX dépendent en fait de fonctionnalités spécifiques à bash(1), ce qui est en général une erreur non intentionnelle de la part du développeur.

csh(1) et son successeur tcsh(1) utilisent une syntaxe notablement différente du shell POSIX, et qui se veut plus proche du langage C. Durant l'époque précédent l'émergence de Linux et la victoire populaire de bash(1), ils ont eu un certain succès, mais il n'y a aucune particulière raison de s'y intéresser de nos jours, à part pour déboguer un rare script issu de temps anciens.

ksh(1) se veut le nouveau standard officiel parmi les systèmes UNIX exclusifs, et possède de nombreuses extensions en faisant un langage bien plus raisonnable que sh pour l'écriture de scripts. Cependant, comme il a longtemps été un logiciel exclusif, il n'a jamais été très répandu, et n'a finalement rien de spécial à offrir, puisque pour tout script avancé, on lui préférera de loin perl(1) ou un autre langage plus avancé, cependant qu'en terme de fonctionnalités interactives, il pêche devant bash(1) ou zsh(1). Pendant que ksh(1) n'était pas libre, une version libre rivale pdksh(1) a été développée, qui n'est pas forcément compatible à 100% avec le ksh(1) officiel.

ash(1) ou dash(1) est la version BSD du shell POSIX, qui colle au plus près à la norme, avec quelques rares extensions. À quoi bon s'y restreindre? À être sûr que vos shell-scripts sont portables. Shell minimal inventé juste pour vérifier la portabilité des scripts: posh(1).

rc(1), issu du système expérimental Plan9, se veut un redesign mieux conçu du shell, de même que Plan9 était un redesign mieux conçu d'Unix (mais qui, n'étant pas libre, a manqué son occasion de percer). Incompatible, pauvre en fonctionnalités, il a fait quelques émules, mais n'est pas si extraordinaire que ça.

Mon préféré est zsh(1), le « shell ultime » qui reprend les meilleures idées de sh(1), bash(1), tcsh(1) et ksh(1), et possède même des modes de compatibilité sh, bash(1), et ksh(1) (et aussi csh mais de façon incomplète). Mais surtout, il possède un grand nombre de fonctionnalités, y compris un mode de complétion programmable, un éditeur intégré, et il est très très configurable et très très extensible avec des modules dynamiquement chargés. Pourquoi s'en priver?

On peut changer le shell par défaut d'un utilisateur avec la commande chsh(8) [46]. Les administrateurs téméraires pourront éditer directement le /etc/passwd [47]. Il faut se reloguer pour que la modification prenne effet.

4.1.3 Quel shell apprendre?

Je vous enseignerai ici le bash(1), qui est le standard sous Linux, tout en précisant parfois dans quelques notes les différences pour le shell POSIX sh et le « shell ultime » zsh(1).

Pour apprendre votre shell en détail, je vous invite toutefois à consulter la page de manuel, à regarder la configuration par défaut sur votre système, à lire les fichiers d'exemples livrés avec le shell, à visionner les sources de shell-scripts utilisant votre shell.

4.1.4 Principe de base du shell

Le shell offre une interface en ligne de commande (anglais: command line): un interacteur permet à l'utilisateur d'éditer une ligne de texte qui spécifie une commande à exécuter.

L'interacteur se reconnaît généralement à l'invite (anglais: prompt) qu'il affiche avant chaque commande. On peut typiquement personnaliser cette invite (grâce à une variable d'environnement nommée PS1 ou PROMPT), de façon à afficher le répertoire courant, l'heure, le nom de l'utilisateur ou de la machine actuelle, ou toute autre information pertinente [48].

Quand on confirme la commande en appuyant sur la touche entrée, le shell lit la ligne qu'il analyse comme un petit programme qu'il exécute. Dans le cas habituel, le plus simple, le programme est une commande simple suivie d'arguments; le shell lance alors ladite commande avec les arguments spécifiés. Ainsi, la commande ls -l /bin appellera typiquement la commande externe ls avec comme arguments l'option -l d'une part et le nom de répertoire /bin d'autre part.

4.1.5 Vos premières commandes

Vous pouvez essayer les commandes suivantes, et utiliser en parallèle konqueror pour essayer de visualiser l'effet de ces commandes:

echo 'Hello, world!'   # affiche des informations
kwrite toto.txt        # édite un fichier
pwd         # affiche le répertoire actuel
ls          # affiche les fichiers dans le répertoire actuel
cd ..       # change le répertoire actuel au répertoire père
pwd         # affiche le répertoire actuel (après cette modification)
ls          # affiche les fichiers dans ce nouveau répertoire
ls -l       # affiche les même fichiers avec davantage d'information
ls -la ~    # le contenu de votre répertoire personnel avec ses fichiers cachés
ls -ld .    # informations sur le répertoire actuel lui-même
ls -F       # ajoute des symboles 
cd /        # va dans le répertoire racine
ls
cd bin      # répertoire des programmes de base du système UNIX
pwd
ls
cd /usr/bin # répertoire avec de nombreux programmes
pwd
ls
cd          # dans quel répertoire vous transporte cette commande?
pwd         # dans votre répertoire personnel!
ls

4.2 Interaction avec le Shell

4.2.1 Édition de ligne avec le shell

Les shells modernes comme bash(1) tcsh(1) zsh(1) gèrent directement l'interaction avec le terminal, et vous offrent de nombreuses facilités pour vous simplifier la vie. Ils peuvent traditionnellement fonctionner selon deux modes, le mode emacs ou le mode vi, dont le comportement imite chacun celui d'un des deux éditeurs traditionnels les plus populaires du monde Unix. Je vous enseignerai le mode emacs, qui est le mode par défaut; lisez le manuel pour le mode vi.

Pour bash(1), l'éditeur intégré s'appelle readline(3) et est configurable via le fichier ~/.inputrc (voir man 1 bash ou man 3 readline). Sous zsh(1), l'éditeur intégré s'appelle zle est avec la commande bindkey à appeler depuis votre ~/.zshrc ou interactivement (voir man 1 zshzle). Notez que de dans le monde UNIX, de nombreux autres programmes interactif, en mode texte ou en mode graphique, utilisent des combinaisons de touches directement inspirées de EMACS, voire utilisent directement readline(3).

Note: par convention du monde EMACS, on écrit C-r pour Contrôle-r, c'est-à-dire laisser enfoncer la touche modificatrice Ctrl, puis la touche R, relâcher le R puis la touche Ctrl. On écrit M-x pour Méta-x, la touche Méta étant souvent celle avec l'étiquette Alt sur les claviers de PC. Dans un terminal, M-x est équivalent à la séquence d'échappement ESC x. La touche d'échappement ESC est aussi équivalente à C-[.

4.2.2 Le shell en mode EMACS

Déplacement sur la ligne actuelle: flèche gauche ou C-b (back) pour caractère précédent, flèche droite ou C-f (forward) pour caractère suivant. Prochain mot: M-f, précédent mot: C-b. Début de ligne avec Home ou C-a, fin de ligne avec End ou C-e.

Effacement: Delete ou C-d pour détruire le caractère suivant, BackSpace pour détruire le caractère précédent [49]. C-u pour effacer toute la ligne, ou C-w pour effacer le mot précédent (non disponibles sous EMACS proprement dit). Effacer la fin de la ligne: C-k

Copier/Couper-Coller: C-y pour récupérer le groupe de caractères dernièrement effacé ou sélectionné. C-SPC pour marquer le début d'une zone à sélectionner, M-w pour sélectionner la zone depuis le marqueur précédent. (ces derniers non disponibles sous bash(1).)

Récupérer les commandes précédentes: flèche haute ou C-p (previous) pour la précédente, flèche basse ou C-n (next) pour la suivante. Recherche en arrière avec C-r ou en avant avec C-s.

Annulation: Annuler la recherche ou toute autre opération spéciale en cours: C-g. Annuler la ligne actuelle ou interrompre une commande: C-c, très pratique si on ne sait plus où on en est question guillemets et parenthèses. Sous emacs(1) ou zsh(1), C-x u pour annuler une modification durant l'édition, plusieurs fois pour en annuler plus (non disponible sous bash(1)).

Entrer un caractère spécial directement: C-v. Vous aurez peut-être en plus à le mettre entre guillemets. Notez que sous EMACS proprement dit, C-v sert à passer à la page suivante cependant que M-v va à la page précédente, et il faut utiliser C-q pour l'effet ci-dessus.

4.2.3 La complétion

Parce qu'ils passent du temps sur leur ligne de commande, les utilisateurs d'UNIX ont de nombreux trucs pour accélérer la frappe. Outre l'édition de ligne et son couper-coller, un truc important est la completion: sous un shell moderne, utilisez la touche Tab pour compléter le nom d'un fichier ou d'une variable que vous avez commencé à taper.

Quand il y a plusieurs fins possibles, le shell complète jusqu'à l'endroit où il faut choisir. Vous pouvez avoir la liste des choix disponibles sous bash(1), en appuyant sur ESC ESC ou Tab. Sous zsh(1), la touche C-d en fin de ligne permet de voir la liste des choix disponibles [50] et la touche Tab propose chacun son tour chacun des choix possibles.

Sous zsh(1), la complétion est particulièrement puissante, et est de plus programmable; vous pouvez ainsi inclure dans votre configuration zsh des complétions adaptées spécifiquement à chacun des programmes que vous utilisez, plutôt que toujours des noms de fichiers.

4.2.4 Le terminal sans éditeur

Les antiques shells standards d'UNIX sh(1) csh(1) ou vieux ksh(1), ainsi que la plupart des programmes conçus pour être invoqués depuis le shell, n'ont pas d'édition de ligne. À défaut de mieux, le gestionnaire de terminal inclus dans le noyau peut gérer pour ces programmes qui ne veulent pas s'en soucier une édition de ligne rudimentaire.

Dans son mode par défaut, le noyau n'enverra au programme lecteur le texte saisi qu'après réception de la touche entrée; cela lui permet de gérer entre autres choses l'effacement arrière (BackSpace) et l'annulation de la ligne (C-u), ainsi que le signal de fin de fichier (C-d en début de ligne). Le noyau enverra un signal d'interruption (kill -INT) au processus courant si on appuie sur la touche C-c, et un signal de fin prématurée (kill -QUIT) si on appuie sur la touche C-\, ou encore un signal d'arrêt momentané (kill -STOP) si on appuie sur la touche C-z (voir la section job control ci-dessous).

On peut ainsi signaler aux programmes qui lisent leur entrée sur l'entrée standard que cette entrée est terminée en leur envoyant une fin de fichier. Si un programme tarde trop à répondre, on peut l'arrêter définitivement avec C-c ou momentanément avec C-z (et le reprendre avec la commande fg).

Pour plus de détails sur les façons d'interagir avec le terminal, voir la section sur le terminal.

4.3 Commandes simples

4.3.1 Invocation d'une commande

La syntaxe la plus simple pour une commande est une liste constituée par le nom de la commande suivi par ses arguments, les composants de la liste étant séparés par un ou plusieurs espacements.

Exemple: ls -l /bin lancera la commande ls avec les arguments -l et /bin.

Si la commande est parmi la liste des commandes internes au shell (anglais: shell built-in commands) telle que documentée dans le manuel, ou une fonction définie par l'utilisateur alors ladite commande sera exécutée.

Sinon, le shell créera un nouveau processus qui exécutera une commande externe. Si le nom de la commande est un chemin d'accès (absolu ou relatif) spécifiant un répertoire, alors il spécifiera le programme à exécuter (utiliser ./foo pour préciser le répertoire actuel). Si ce nom ne comporte pas de répertoire, alors le shell ira chercher dans chacun des répertoires définis dans la variable d'environnement PATH si un programme de ce nom existe, et exécutera le premier [51]. Si aucun programme n'est trouvé dans le $PATH, ou que l'utilisateur ne possède pas les droits d'accès en exécution sur ledit programme, alors le shell renvoie une erreur [52].

4.3.2 Commandes pour s'en sortir avec les fichiers

Les commandes suivantes permettent de manipuler les fichiers à partir du shell textuel. Pour des cas simples, cela n'apporte rien par rapport à l'interface graphique; par contre, l'interface textuelle pourra être automatisée et fiabilisée.

4.3.3 Conventions d'appel pour les commandes

Une commande prend un nom de commande suivi de paramètres, dont des options. [55]

Les options conventionnellement généralement par un caractère '-'. De nombreuses commandes acceptent de grouper plusieurs options simples derrière un seul '-': cp -ax /foo /bar au lieu de cp -a -x /foo /bar, ou tar -zxvf foo.tar.gz au lieu de tar -z -x -v -f foo.tar.gz. De nombreuses commandes ont des options avec un nom long, et la convention aujourd'hui dominante est que de telles options doivent être introduites par deux caractères '-': cp --help. De nombreux programmes suivent la convention supplémentaire selon laquelle le caractère '+' est employé pour désactiver l'option activée avec le caractère '-'. Certaines options sont suivies d'un chemin d'accès à un fichier, d'un nombre ou d'un autre paramètre.

La seule façon fiable de savoir comment fonctionne une commande, c'est de lire sa documentation: man nom_de_commande. Les commandes internes (anglais: builtin commands) de votre shell sont décrites dans la page de manuel dudit shell: man bash ou man zshbuiltins.

Le shell lui-même possède des options (très nombreuses dans le cas de zsh(1)), documentées dans le manuel. À fins d'inspection (pour débogage notamment), on peut appeler le shell avec l'option -x pour tracer les commandes. Ou on peut l'activer durant l'exécution avec set -x ou la désactiver avec set +x. Sous bash(1), on préférera shopt pour changer les options. Sous zsh(1), on préférera setopt. Notez sous zsh(1) l'option LOCAL_OPTIONS pour ne changer des options qu'à l'intérieur de la fonction actuelle.

4.4 Expansion des commandes par le shell