Le terminal et le système de fichiers
Ce que ce chapitre apporte
- Expliquer ce qu'apporte une ligne de commande par rapport à un explorateur de fichiers.
- Lire et écrire un chemin absolu ou relatif, et savoir où l'on se trouve.
- Enchaîner les commandes de base pour se déplacer, lister, copier, déplacer et supprimer.
- Rediriger l'entrée, la sortie et les erreurs d'un programme, et brancher deux programmes l'un sur l'autre.
- Reconnaître ce que le shell transforme avant d'appeler la commande.
- Lire des droits Unix et dire si une opération va aboutir.
- Retrouver la variable PATH et expliquer pourquoi une commande installée reste introuvable.
Une ligne de commande, pour quoi faire
Un explorateur de fichiers montre un état. Une ligne de commande décrit une action. La différence se voit dès qu'il y a plus d'un fichier à traiter.
Renommer trois cents images en ajoutant la date devant leur nom prend une ligne au terminal et une demi-heure à la souris. Surtout, la ligne se garde. On peut la relire six mois plus tard, la corriger, l'envoyer à un collègue, la mettre dans un script qui tournera chaque nuit. Le geste à la souris ne laisse rien.
Le shell est le programme qui lit ce que vous tapez, l'interprète, lance les programmes demandés et affiche leur résultat. bash et zsh sous Linux et macOS, PowerShell sous Windows.
Le terminal, lui, n'est que la fenêtre. Il affiche le texte et transmet les frappes. On confond souvent les deux, sans grande conséquence, mais la distinction sert dès qu'on veut lancer un shell sans fenêtre, par exemple depuis un script.
L'arborescence et les chemins
Un système de fichiers est un arbre. Un seul sommet, la racine, notée / sous Linux et macOS, C:\ sous Windows. Chaque dossier contient des fichiers et d'autres dossiers, et rien ne peut avoir deux parents.
Un chemin absolu part de la racine et désigne toujours le même fichier : /home/lea/projet/donnees.csv.
Un chemin relatif part du dossier courant : projet/donnees.csv désigne un fichier différent selon l'endroit d'où on le lit.
Trois notations reviennent partout :
| Notation | Sens |
|---|---|
. | le dossier courant |
.. | le dossier parent |
~ | le dossier personnel de l'utilisateur |
Le dossier courant est la donnée cachée de toute commande relative. pwd l'affiche, et c'est la première chose à vérifier quand une commande ne trouve pas un fichier qui existe pourtant.
Résoudre un chemin à la main
Le système normalise un chemin avant de l'ouvrir : il applique les . et les .. pour obtenir une suite de dossiers sans détour. L'algorithme tient en quelques lignes, et l'écrire une fois évite bien des erreurs de raisonnement.
Le dernier cas mérite un arrêt : remonter au-dessus de la racine ne provoque pas d'erreur, on reste à la racine. C'est aussi le comportement du système.
Manipuler des fichiers
Deux réflexes qui évitent le pire : lister d'abord avec
ls ce que la commande va toucher, et se méfier de l'espace de trop. rm -rf / mon-dossier et rm -rf /mon-dossier ne détruisent pas la même chose.
Les noms qui contiennent une espace doivent être protégés, sinon le shell y voit deux arguments : rm "mon rapport.txt" ou rm mon\ rapport.txt.
Entrée, sortie, erreurs
Tout programme lancé depuis un shell reçoit trois canaux : une entrée standard, une sortie standard, une sortie d'erreur. Par défaut, l'entrée est le clavier et les deux sorties sont l'écran. Ces branchements se changent.
| Écriture | Effet |
|---|---|
commande > fichier | la sortie va dans le fichier, qui est écrasé |
commande >> fichier | la sortie s'ajoute à la fin du fichier |
commande 2> erreurs.log | seules les erreurs partent dans le fichier |
commande < entree.txt | le fichier tient lieu de clavier |
commande1 | commande2 | la sortie de la première devient l'entrée de la seconde |
Le tube, la barre verticale, est l'idée qui fait la puissance de l'ensemble. Chaque outil fait une chose, et on les assemble.
Cette seconde ligne se lit de gauche à droite comme une phrase : prendre la troisième colonne, trier, compter les doublons, trier par nombre décroissant, garder les cinq premières. Aucun des cinq programmes ne connaît les autres.
Erreur : fichier introuvable écrit sur la sortie d'erreur, pas sur la sortie standard. Donc commande > resultat.txt laisse le message d'erreur à l'écran, et commande | grep motif ne le voit pas.C'est voulu : on peut ainsi rediriger un résultat sans y mélanger les avertissements.
Ce que le shell change avant d'appeler la commande
Une source classique de confusion : certaines transformations sont faites par le shell, pas par le programme.
ls ne reçoit jamais *.csv. Le shell développe le motif en la liste des fichiers correspondants, puis appelle ls avec cette liste. Si aucun fichier ne correspond, bash transmet le motif tel quel, et ls se plaint d'un fichier nommé *.csv.
| Motif | Correspond à |
|---|---|
* | n'importe quelle suite de caractères, y compris vide |
? | exactement un caractère |
[0-9] | un caractère parmi ceux listés |
Les guillemets bloquent ce développement. grep "*.csv" liste.txt cherche bien le texte *.csv, alors que sans guillemets le shell aurait remplacé le motif avant que grep ne voie quoi que ce soit.
Les droits
Sous Linux et macOS, chaque fichier porte trois groupes de trois droits : lecture, écriture, exécution, pour le propriétaire, le groupe et les autres.
$ ls -l script.sh
-rwxr-xr-- 1 lea etudiants 482 12 sep 09:14 script.sh
Le premier caractère dit le type, - pour un fichier ordinaire, d pour un dossier. Viennent ensuite rwx pour Léa, r-x pour le groupe etudiants, r-- pour tous les autres. Léa peut modifier et exécuter, son groupe peut exécuter mais pas modifier, les autres peuvent seulement lire.
Sur un dossier, le droit d'exécution n'a pas le sens qu'on croit : il autorise à traverser le dossier, donc à accéder à ce qu'il contient. Un dossier lisible mais non traversable laisse voir les noms sans permettre d'ouvrir les fichiers.
sudo exécute la commande en administrateur. Il règle le problème du droit manquant, et masque la vraie question : pourquoi ce fichier ne vous appartient-il pas ?Un projet personnel dont les fichiers appartiennent à l'administrateur, parce qu'une commande a été lancée avec
sudo un jour, deviendra pénible pour toutes les suivantes.
PATH, ou pourquoi la commande reste introuvable
Quand vous tapez python, le shell ne fouille pas le disque. Il parcourt, dans l'ordre, une liste de dossiers donnée par la variable d'environnement PATH, et prend le premier exécutable de ce nom.
Deux conséquences pratiques. Un outil installé dans un dossier absent du PATH reste introuvable, alors que le fichier existe. Et si deux versions sont installées, c'est l'ordre du PATH qui tranche, ce qui explique la moitié des « mais je viens de l'installer ».
Une variable d'environnement est un couple nom-valeur transmis par le shell à tout programme qu'il lance. PATH, HOME, LANG en sont, et les applications y lisent souvent leur configuration.
La valeur disparaît à la fermeture du terminal. Pour la rendre permanente, on l'écrit dans le fichier de configuration du shell, ~/.bashrc ou ~/.zshrc. Pour un projet, on préférera un fichier .env lu par l'application, ce que le chapitre sur les dépendances reprendra.
Exercices type
cd projet répond « No such file or directory », alors que le dossier existe. Que vérifier ?
D'abord pwd. Un chemin relatif dépend du dossier courant, et projet n'existe peut-être pas ici.
Ensuite ls -a, pour deux raisons : vérifier l'orthographe exacte, majuscules comprises, car les systèmes de fichiers Linux distinguent la casse, et voir si le nom contient une espace ou un caractère accentué qui demanderait des guillemets.
Enfin ls -l, pour vérifier que le dossier est traversable, c'est-à-dire qu'il porte le droit x.
Quelle différence entre > et >> ?
> remplace le contenu du fichier. Si le fichier existait, son contenu précédent est perdu, et il l'est même si la commande échoue ensuite, car le shell vide le fichier avant de lancer le programme.
>> ajoute à la fin.
Pour un journal qu'on alimente à chaque exécution, c'est >>. Pour un résultat qu'on veut voir remplacé à chaque fois, c'est >.
grep erreur *.log | wc -l ne compte rien alors que les fichiers contiennent des erreurs. Pourquoi ?
Trois causes possibles, à écarter dans l'ordre.
La casse : grep distingue majuscules et minuscules. Si les journaux écrivent ERREUR, il faut grep -i.
Le développement du motif : si aucun fichier .log n'existe dans le dossier courant, le shell transmet *.log littéralement et grep cherche dans un fichier qui n'existe pas. Le message d'erreur, lui, part sur la sortie d'erreur et n'entre pas dans le tube, donc rien n'apparaît.
L'endroit : les journaux sont peut-être dans un sous-dossier, et il faut alors grep -r erreur ..
Pourquoi rm -rf mérite-t-il un instant d'attention à chaque usage ?
Parce que l'opération est immédiate, récursive et définitive : il n'y a ni confirmation ni corbeille.
Le danger tient surtout à la construction de l'argument. Une espace mal placée, une variable vide dans un script, un chemin relatif alors qu'on n'est pas dans le dossier attendu, et la commande efface autre chose que prévu.
La parade est simple : remplacer d'abord rm par ls pour voir la liste exacte des fichiers visés, puis relancer avec rm.
Un collègue vous envoie une commande qui marche chez lui et pas chez vous. Par où commencer ?
Par which sur le programme appelé. S'il est introuvable, l'outil n'est pas installé, ou il est installé dans un dossier absent de votre PATH.
S'il est trouvé, comparez les versions : commande --version de part et d'autre. Les options d'un outil changent d'une version à l'autre.
Vérifiez enfin les chemins de la commande, souvent relatifs, donc dépendants du dossier courant, et les variables d'environnement qu'elle utilise, qui ne sont pas définies chez vous.
À quoi sert un tube, alors qu'on pourrait écrire un fichier intermédiaire ?
À trois choses.
Le résultat intermédiaire n'existe jamais sur le disque, donc rien à nettoyer et rien qui traîne.
Les deux programmes tournent en même temps, le second consommant ce que le premier produit. Un fichier de dix gigaoctets peut ainsi être filtré sans jamais être stocké deux fois.
Et l'écriture reste linéaire, donc lisible : une suite de transformations dans l'ordre où on les pense.
La méthode
- Vérifie où tu es avec
pwdavant toute commande qui utilise un chemin relatif. - Complète avec Tab plutôt que de retaper un nom : si la complétion ne vient pas, le chemin est faux.
- Liste avant de détruire. Remplace
rmparlspour voir la cible réelle. - Protège par des guillemets tout argument qui contient une espace ou un caractère spécial.
- Assemble de petits outils avec un tube plutôt que de chercher la commande qui fait tout.
- Regarde la sortie d'erreur avant de conclure qu'un programme n'a rien affiché.
- En cas de commande introuvable, interroge
whichetecho $PATHavant de réinstaller quoi que ce soit.
En résumé
- Le shell interprète la ligne, le terminal n'est que la fenêtre.
- Un chemin absolu part de la racine, un chemin relatif du dossier courant.
.,..et~désignent le dossier courant, le parent et le dossier personnel.- Tout programme dispose de trois canaux : entrée, sortie, erreur, redirigeables séparément.
- Le tube branche la sortie d'un programme sur l'entrée du suivant.
- Le shell développe les motifs avant d'appeler la commande. Les guillemets l'en empêchent.
- Les droits Unix se lisent en trois groupes de
rwx. Sur un dossier,xautorise la traversée. PATHfixe quel exécutable est lancé, et dans quel ordre les dossiers sont cherchés.
Et ensuite ? Le terminal sait tout faire, mais il ne connaît pas votre code. Le chapitre suivant s'occupe de l'outil qui, lui, le connaît : l'éditeur.