Aller au contenu
Le terminal et le shell

Le terminal et le shell

100 Comprendre ⏱ 1 h linuxbashubuntu

À la fin, vous saurez

  • Distinguer terminal, émulateur de terminal, console et shell
  • Décomposer une commande en nom, options et arguments, et utiliser -- pour terminer les options
  • Dire si une commande est interne, externe, un alias ou un mot-clé, avec type
  • Trouver l'aide d'une commande inconnue avec man, apropos, help et --help
  • Retrouver et réutiliser une commande avec l'historique et les raccourcis de Readline, sans y laisser de secret
  • Prévoir l'effet des guillemets simples, doubles et de la barre oblique inverse

Testé avec bash 5.2.21 coreutils 9.4 man-db 2.12.0 strace 6.8 ubuntu 24.04 , vérifié le 5 octobre 2026

Pourquoi

Vous avez votre accès à sig-app-1. Un collègue vous envoie trois commandes à lancer pour vérifier l'état de Signalements, vous les collez, et la troisième répond command not found. Il vous dit : « c'est un alias chez moi ». Vous en lancez une autre, trouvée dans une documentation, qui contient un mot de passe : elle restera dans un fichier sur le serveur, lisible plus tard par d'autres. Une troisième, copiée d'un forum, commence par curl ... | sudo sh : vous ne savez pas ce qu'elle fait, et elle va s'exécuter avec tous les droits.

Le terminal donne une impression de magie noire tant qu'on ne sait pas qui fait quoi : qui lit ce que vous tapez, qui trouve le programme, qui interprète les guillemets, qui retient l'historique. Cette leçon met un nom sur chaque acteur. Une fois qu'on sait que le shell transforme votre ligne avant de lancer quoi que ce soit, la plupart des erreurs « inexplicables » deviennent prévisibles, et l'on sait où chercher l'aide d'une commande sans quitter le serveur.

Les concepts

Terminal, console, shell : qui fait quoi

Quatre mots sont souvent employés l'un pour l'autre. Ils désignent des choses différentes :

TermeCe que c'estExemples
TerminalÀ l'origine, un appareil physique (clavier et écran, ou télétype) relié à un ordinateur. Aujourd'hui, l'interface en mode texte par laquelle un programme reçoit vos frappes et affiche ses sortiesLe mot tty (teletypewriter) en est l'héritage
Émulateur de terminalUn programme graphique qui imite un terminal dans une fenêtreGNOME Terminal, Konsole, Windows Terminal, iTerm2
ConsoleLe terminal attaché directement à la machine (écran et clavier physiques, ou console série d'une machine virtuelle chez un fournisseur de cloud)Les terminaux virtuels de Linux (Ctrl+Alt+F3), la console web de Scaleway
ShellLe programme qui lit vos commandes, les interprète et les fait exécuterBash, Zsh, Dash, Fish

Le terminal transporte des caractères ; le shell leur donne un sens. Quand vous vous connectez à sig-app-1 par SSH depuis votre émulateur de terminal, le serveur SSH crée pour vous un pseudo-terminal (une paire de périphériques virtuels qui se comporte comme un terminal, décrite par la page pty(7)), puis lance votre shell relié à ce pseudo-terminal. Vos frappes traversent le réseau, arrivent dans le pseudo-terminal, et le shell les lit.

Le shell par défaut d'Ubuntu et de Debian pour les comptes interactifs est Bash (Bourne Again SHell), le shell du projet GNU, né en 1989 pour remplacer le shell de Bourne d'Unix. Les scripts système de Debian et d'Ubuntu utilisent souvent /bin/sh, qui y est Dash, un shell plus petit et plus rapide qui se limite à la norme POSIX. Zsh est le shell par défaut de macOS. Ce cours utilise Bash, que vous trouverez sur pratiquement tous les serveurs.

Se connecter à sig-app-1

Depuis votre poste (Linux, macOS, ou Windows avec le client OpenSSH intégré), la connexion tient en une commande :

$ ssh camille@sig-app-1.exemple.fr

camille est votre compte sur le serveur, après @ vient le nom (ou l'adresse IP) de la machine. À la toute première connexion, le client affiche l'empreinte de la clé du serveur et vous demande de confirmer : c'est le moment de vérifier qu'on vous a communiqué la même empreinte par un autre canal. Le cours SSH explique pourquoi, et comment se connecter par clé plutôt que par mot de passe. Une fois connecté, vous êtes devant l'invite du shell.

L'invite

camille@sig-app-1:~$

L'invite (prompt) de Bash sur Ubuntu se lit de gauche à droite : l'utilisateur (camille), la machine (sig-app-1), le répertoire courant (~, raccourci pour votre répertoire personnel, leçon 3), puis $. Ce dernier caractère devient # quand le shell tourne sous le compte root. Gardez ce réflexe : un # dans l'invite signifie que chaque commande a tous les droits. Dans les blocs de commandes de ce cours, l'invite est abrégée en $ (utilisateur normal) ou # (root), et ce qui suit est ce que vous tapez.

L'invite est définie par la variable PS1, que la leçon 7 apprend à modifier. Afficher le nom de la machine dans l'invite n'est pas décoratif : c'est ce qui vous évite de lancer sur le serveur de production la commande destinée à la recette.

Anatomie d'une commande

$ ls -l -h --sort=time /var/log

Le shell découpe cette ligne en mots séparés par des espaces :

  • le premier mot est le nom de la commande (ls) ;
  • les mots qui commencent par - sont des options, qui modifient le comportement. Les options courtes sont une lettre après un tiret (-l, -h) et peuvent se grouper : -lh équivaut à -l -h. Les options longues, une convention GNU, sont des mots après deux tirets (--sort=time, --human-readable) : plus lisibles dans un script ;
  • certaines options prennent une valeur : --sort=time, ou -n 20 pour head ;
  • les autres mots sont des arguments, le plus souvent ce sur quoi la commande agit (/var/log).

Ces conventions sont fixées par les règles de syntaxe des utilitaires de la norme POSIX. L'une d'elles mérite d'être connue dès maintenant : le premier argument -- marque la fin des options. Tout ce qui suit est un argument, même s'il commence par un tiret. Vous en aurez besoin le jour où un fichier s'appellera -l ou --help (cela arrive, par erreur de frappe ou par malveillance) :

$ touch -- -l
$ ls
-l  journaux  lien  note.txt
$ rm -l
rm: invalid option -- 'l'
Try 'rm ./-l' to remove the file '-l'.
Try 'rm --help' for more information.
$ rm -- -l

touch crée un fichier vide ; sans --, il aurait pris -l pour une option. rm -l échoue : rm croit recevoir l'option -l, qu'il ne connaît pas, et a la délicatesse de suggérer la parade. rm -- -l supprime bien le fichier. La forme rm ./-l fonctionne aussi, puisque ./-l ne commence pas par un tiret. La leçon 3 montre pourquoi ce piège devient dangereux avec les motifs *.

Toutes les commandes ne respectent pas ces conventions : find a des options d'un seul tiret mais de plusieurs lettres (-name), tar accepte des options sans tiret (tar xzf), dd utilise clé=valeur. Leur page de manuel fait foi.

Commandes internes, externes, alias, mots-clés

Quand vous tapez un nom de commande, le shell cherche ce qu'il désigne, dans cet ordre :

  1. un alias : un raccourci textuel défini dans votre configuration (ll pour ls -alF sur Ubuntu) ;
  2. un mot-clé du langage du shell (if, for, while...) ;
  3. une fonction définie dans le shell ;
  4. une commande interne (builtin) : une commande exécutée par le shell lui-même, sans lancer de programme (cd, echo, export, history) ;
  5. une commande externe : un fichier exécutable trouvé dans l'un des répertoires de la variable PATH (ls est le fichier /usr/bin/ls).

La commande interne type répond à la question « qu'est-ce que c'est ? » :

$ type cd
cd is a shell builtin
$ type ls
ls is aliased to `ls --color=auto'
$ type ll
ll is aliased to `ls -alF'
$ type if
if is a shell keyword
$ type -a echo
echo is a shell builtin
echo is /usr/bin/echo
echo is /bin/echo

type -a affiche toutes les définitions, dans l'ordre où le shell les essaierait. echo existe à la fois en interne et en programme externe ; le shell prendra l'interne. Le programme apparaît deux fois parce que /bin est, sur Ubuntu, un lien vers /usr/bin (leçon 3).

Pourquoi cd est-il forcément interne ? Parce que changer de répertoire courant modifie l'état du shell lui-même. Un programme externe tourne dans un processus séparé (voir Sous le capot) : il changerait son propre répertoire courant, puis se terminerait, et votre shell n'aurait pas bougé. C'est le même raisonnement pour export, qui modifie l'environnement du shell (leçon 7).

which ls répond /usr/bin/ls : which est un programme externe qui ne cherche que dans PATH, et ignore donc alias, fonctions et commandes internes. Préférez type, qui dit ce que le shell va réellement exécuter. Pour mémoriser l'emplacement des commandes externes, Bash tient une table, que hash affiche :

$ hash
hits	command
   1	/usr/bin/ls
   1	/usr/bin/cat

Si vous installez un programme du même nom ailleurs dans le PATH, Bash continuera d'utiliser l'ancien chemin mémorisé : hash -r vide la table.

Enfin, un message command not found veut dire qu'aucune des cinq recherches n'a abouti : faute de frappe, alias défini chez un collègue et pas chez vous, ou programme non installé. Sur Ubuntu, le message suggère souvent le paquet qui fournit la commande (leçon 12).

Trouver de l'aide sans quitter le serveur

Vous ne retiendrez jamais toutes les options de toutes les commandes, et personne ne le fait. Ce qu'il faut retenir, c'est où chercher :

OutilPour quoiExemple
commande --helpRésumé des options, convention GNU, presque universells --help
man commandeLa page de manuel complète, la référenceman ls
man -k mot ou apropos motChercher une commande par mot-clé dans les descriptionsapropos -s 8 "user account"
help commandeL'aide des commandes internes de Bash, qui n'ont pas de page de manuel proprehelp cd
info commandeLa documentation longue de certains projets GNU (coreutils, bash)info coreutils
/usr/share/doc/<paquet>/Documentation, exemples et journal des changements fournis par le paquetls /usr/share/doc/openssh-server

Le manuel est découpé en sections numérotées, que man man décrit :

SectionContenu
1Commandes utilisateur (ls, cp, passwd)
2Appels système (openat, execve, leçon 1)
3Fonctions de bibliothèques (printf du langage C)
4Fichiers spéciaux (/dev/null)
5Formats de fichiers (/etc/passwd, os-release)
7Divers : conventions, protocoles, vue d'ensemble (signal, hier)
8Commandes d'administration (useradd, ip)

Un même nom peut exister dans plusieurs sections. On le voit avec man -f :

$ man -f passwd
passwd (1)           - change user password
passwd (1ssl)        - OpenSSL application commands
passwd (5)           - the password file

man passwd ouvre la première trouvée, la commande (section 1). Pour lire la description du fichier /etc/passwd, il faut demander la section 5 : man 5 passwd. C'est aussi ce que signifie la notation passwd(5) dans les documentations. Et pour chercher une commande dont on ignore le nom :

$ apropos -s 8 "user account"
userdel (8)          - delete a user account and related files
usermod (8)          - modify a user account

Dans man, les touches sont celles du visualiseur less (leçon 5) : flèches et Espace pour avancer, /mot puis Entrée pour chercher, n pour l'occurrence suivante, q pour quitter. Une page de manuel se lit dans l'ordre : NAME, SYNOPSIS (la syntaxe, où [...] indique un élément facultatif et ... un élément répétable), DESCRIPTION, puis les options, et souvent tout en bas des EXAMPLES et un SEE ALSO qui renvoie aux pages voisines.

Note

Les images « minimisées » d'Ubuntu, courantes dans le cloud et les conteneurs, retirent les pages de manuel pour gagner de la place : man affiche alors un message qui le signale. La commande unminimize les restaure. Dans un conteneur, consultez plutôt les pages en ligne sur man7.org ou manpages.ubuntu.com, en prenant la version qui correspond à votre distribution.

L'historique

Bash garde les commandes que vous tapez dans une liste, sauvegardée à la fermeture du shell dans ~/.bash_history. Les outils :

  • les flèches haut et bas parcourent l'historique ;
  • Ctrl+R lance une recherche inverse : tapez un fragment (journalctl), la dernière commande qui le contient s'affiche ; Ctrl+R à nouveau remonte à la précédente, Entrée l'exécute, une flèche la place sur la ligne pour la modifier ;
  • history affiche la liste numérotée, history 20 les vingt dernières ;
  • !! est remplacé par la commande précédente, !42 par la commande numéro 42. L'usage le plus connu : vous lancez une commande qui exige des droits, elle échoue, et sudo !! la relance avec sudo (leçon 8). Bash affiche la commande obtenue avant de l'exécuter.

Le comportement de l'historique est réglé par des variables (leçon 7). Sur Ubuntu, le fichier ~/.bashrc fourni à chaque nouvel utilisateur fixe HISTSIZE=1000 (commandes gardées en mémoire), HISTFILESIZE=2000 (lignes gardées dans le fichier) et HISTCONTROL=ignoreboth. Le manuel de Bash explique cette dernière valeur : ignoreboth combine ignoredups, qui ne garde pas une ligne identique à la précédente, et ignorespace, qui ne garde pas les lignes qui commencent par une espace. Ce détail a une conséquence de sécurité, vue plus bas.

La complétion et les raccourcis de la ligne

La touche Tab complète ce que vous tapez : un nom de commande, un chemin de fichier, et pour de nombreuses commandes leurs options et arguments (le paquet bash-completion d'Ubuntu ajoute par exemple la complétion des noms de services pour systemctl). Un appui ne complète que s'il n'y a qu'une possibilité ; deux appuis listent les possibilités. Utilisez-la tout le temps : elle évite les fautes de frappe, et une complétion qui ne vient pas est un signal (le fichier n'existe pas là où vous le croyez).

L'édition de la ligne est assurée par la bibliothèque Readline, avec des raccourcis hérités d'Emacs. Les plus utiles, tels que les liste bind -p dans Bash :

RaccourciAction
Ctrl+A / Ctrl+EDébut / fin de ligne
Ctrl+WEfface le mot avant le curseur
Ctrl+U / Ctrl+KEfface du curseur au début / à la fin de la ligne
Ctrl+LEfface l'écran
Ctrl+RRecherche dans l'historique
Ctrl+DSur une ligne vide : fin de saisie, ferme le shell (donc la session SSH)
Ctrl+CInterrompt la commande en cours (un signal, leçon 10) ou abandonne la ligne

Guillemets et échappement

Avant d'exécuter une commande, le shell transforme la ligne (voir Sous le capot) : il remplace $USER par la valeur de la variable, * par une liste de fichiers, et découpe le tout en mots aux espaces. Les guillemets servent à contrôler ces transformations :

$ echo "Bonjour $USER"
Bonjour camille
$ echo 'Bonjour $USER'
Bonjour $USER
$ echo Bonjour\ \ \ le   monde
Bonjour   le monde
$ echo "Bonjour   le   monde"
Bonjour   le   monde
  • Les guillemets doubles gardent les espaces et empêchent le découpage en mots et l'interprétation de *, mais laissent le shell remplacer les variables ($USER) et les substitutions de commandes.
  • Les guillemets simples neutralisent tout : le texte est pris littéralement, $USER compris. Utilisez-les pour un texte qui contient $, ! ou \, comme un mot de passe ou une expression régulière.
  • La barre oblique inverse (\) neutralise le seul caractère qui la suit : \ est une espace qui ne sépare pas les mots. Dans la troisième commande, les trois espaces échappées sont gardées, mais les espaces non protégées entre le et monde ne servent qu'à séparer deux arguments, qu'echo réaffiche séparés par une seule espace.

Règle pratique : entourez de guillemets doubles toute variable ("$fichier"), sauf raison précise de ne pas le faire. Un nom de fichier qui contient une espace, sans guillemets, devient deux arguments. Le cours Bash pour l'automatisation reprend ce sujet en détail.

En pratique

Ouvrez une session sur votre machine d'entraînement et suivez. Les sorties ont été produites sur une Ubuntu 24.04 avec LC_ALL=C.UTF-8 (messages en anglais) ; seul le nom de l'utilisateur a été remplacé par camille, et les numéros de processus et adresses mémoire seront différents chez vous.

Identifier votre shell et votre terminal

$ echo $SHELL
/bin/bash
$ echo $0
$ tty

$SHELL contient votre shell de connexion, tel qu'il est inscrit dans /etc/passwd (leçon 8). $0 est le nom du programme en cours : dans une session SSH, il vaut -bash, et ce tiret initial signale un shell de connexion, ce qui change les fichiers de configuration qu'il lit (leçon 7) ; dans un terminal ouvert depuis un bureau graphique, il vaut simplement bash. tty affiche le terminal relié à votre shell : dans une session SSH, un chemin de la forme /dev/pts/0, un pseudo-terminal (pts) créé par le serveur SSH ; sur la console de la machine, un chemin de la forme /dev/tty1.

Explorer une commande inconnue

Votre collègue vous demande de « regarder avec ss qui écoute sur le port 8000 ». Vous ne connaissez pas ss. Démarche :

$ type ss
ss is /usr/bin/ss
$ man -f ss
ss (8)               - another utility to investigate sockets
$ ss --help | head -5
$ man ss

type dit que c'est un programme externe ; man -f donne la section (8, administration) et la description ; --help donne un aperçu rapide ; la page de manuel, la description complète. Dans man ss, tapez /EXAMPLES puis Entrée pour sauter aux exemples, puis q. Vous y trouverez ss -t -a (toutes les connexions TCP). La commande attendue est ss -ltn : -l les sockets en écoute, -t TCP, -n les numéros de port plutôt que les noms de services. Le cours Le modèle TCP/IP y reviendra.

Pour une commande interne, man ne sert à rien : la page man cd n'existe pas en tant que telle ou renvoie à la page générale de Bash. Utilisez help :

$ help cd | head -2
cd: cd [-L|[-P [-e]] [-@]] [dir]
    Change the shell working directory.

Utiliser l'historique sans y laisser de secret

Lancez quelques commandes, puis cherchez-en une :

$ history | tail -5

Appuyez sur Ctrl+R, tapez man, puis Ctrl+R encore pour remonter, et Ctrl+G pour abandonner la recherche. Puis vérifiez l'effet de ignorespace :

$ echo $HISTCONTROL
ignoreboth
$  echo "commande avec une espace devant"
$ history | tail -2

La commande précédée d'une espace n'apparaît pas dans history : elle ne sera pas non plus écrite dans ~/.bash_history. C'est la façon de taper une commande qui contient une information sensible quand on ne peut pas faire autrement. Mieux vaut encore ne pas en taper du tout : la plupart des outils savent lire un secret dans un fichier ou le demander sans l'afficher (read -s dans un script).

Ce que voit le noyau

Vérifiez ce que la leçon 1 annonçait : pour lancer un programme externe, le shell crée un nouveau processus. Avec strace, en suivant les processus enfants (-f) et en ne gardant que la création de processus et le lancement de programmes :

$ strace -f -e trace=execve,clone,clone3 bash -c 'ls note.txt; true'
execve("/usr/bin/bash", ["bash", "-c", "ls note.txt; true"], 0x7fff64df2ee8 /* 68 vars */) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLDstrace: Process 591385 attached
, child_tidptr=0x7811cb0d4a10) = 591385
[pid 591385] execve("/usr/bin/ls", ["ls", "note.txt"], 0x648355157530 /* 68 vars */) = 0
note.txt
[pid 591385] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=591385, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++

Lecture ligne à ligne :

  1. execve("/usr/bin/bash", ...) : strace lance un Bash, auquel on donne la ligne ls note.txt; true (l'option -c passe une commande à exécuter).
  2. clone(...) = 591385 : Bash se duplique. Le nouveau processus, numéro 591385, est une copie du shell.
  3. [pid 591385] execve("/usr/bin/ls", ["ls", "note.txt"], ...) : la copie se remplace par le programme /usr/bin/ls, avec la liste d'arguments ["ls", "note.txt"]. Les guillemets, les espaces, l'alias éventuel ont disparu : le noyau ne reçoit qu'un chemin de programme et un tableau de mots.
  4. ls affiche note.txt et se termine avec le code 0.
  5. SIGCHLD : le noyau prévient le shell parent que son enfant s'est terminé, avec son code de sortie (si_status=0).
  6. true est une commande interne : aucun clone ni execve pour elle. Bash l'exécute lui-même, puis se termine.

Sous le capot

Créer un processus, puis lancer un programme. Sous Unix, lancer un programme se fait en deux temps, et c'est ce que montre la trace. D'abord le processus se duplique (fork, que la bibliothèque C implémente sous Linux avec l'appel système clone) : l'enfant hérite d'une copie de tout ce qu'a le parent, son répertoire courant, ses variables d'environnement, ses fichiers ouverts. Ensuite l'enfant appelle execve, qui remplace son programme par un autre, sans changer de numéro de processus ni perdre ce qu'il a hérité. Ce découpage explique deux comportements qui étonnent les débutants : un programme lancé depuis le shell hérite de votre environnement et de votre répertoire courant (leçon 7), et il ne peut jamais modifier ceux du shell en retour, d'où les commandes internes cd et export.

Ce que fait le shell de votre ligne. Entre la touche Entrée et l'appel à execve, Bash fait beaucoup de travail. Le manuel le décrit ainsi : il découpe la ligne en mots et en opérateurs (|, ;, >...), remplace les alias, puis applique les expansions dans un ordre fixe :

  1. expansion des accolades (fichier.{log,txt} devient fichier.log fichier.txt, leçon 3) ;
  2. expansion du tilde (~ devient /home/camille), des paramètres et variables ($USER), arithmétique ($((2+3))) et substitution de commande ($(date)), de gauche à droite ;
  3. découpage en mots (word splitting) du résultat des expansions non protégées par des guillemets ;
  4. expansion des chemins (*.log devient la liste des fichiers correspondants, leçon 3) ;
  5. suppression des guillemets.

Puis il met en place les redirections (leçon 6), trouve la commande, et l'exécute. L'ordre explique la règle des guillemets : une variable qui contient mon fichier.txt, utilisée sans guillemets, est remplacée à l'étape 2, puis coupée en deux mots à l'étape 3. Entre guillemets doubles, l'étape 3 ne s'applique pas. Et il explique pourquoi echo '$USER' affiche $USER : les guillemets simples empêchent l'étape 2, puis disparaissent à l'étape 5.

Le terminal a sa propre intelligence. Ctrl+C n'est pas traité par le shell mais par le pilote de terminal du noyau, qui le transforme en signal d'interruption envoyé aux processus au premier plan (leçon 10). De même, Ctrl+D est interprété comme « fin de fichier » quand la ligne est vide : le shell lit une entrée terminée, et se ferme.

Pièges courants

command not found après un copier-coller. La commande vient d'un alias ou d'une fonction du collègue, ou d'un paquet non installé chez vous. type nom chez lui dit ce qu'elle est vraiment.

Les guillemets typographiques. Un traitement de texte, une messagerie ou certains sites remplacent " par “ ” et ' par ’. Le shell ne les reconnaît pas comme guillemets : la commande échoue de façon étrange, ou pire, réussit avec des arguments différents. Copiez depuis un bloc de code, pas depuis un paragraphe.

Le tiret de trop ou de moins. -help n'est pas --help : pour beaucoup de commandes GNU, -help est lu comme les options -h -e -l -p groupées. Et un nom de fichier qui commence par - est lu comme une option : -- ou ./.

Fermer le shell par erreur. Ctrl+D sur une ligne vide ferme la session SSH. Rien de grave, mais une commande longue lancée au premier plan s'arrête avec elle (leçon 10 pour la garder en vie).

Confondre $ et # dans une documentation. Une ligne qui commence par # dans un bloc de commandes indique qu'il faut être root, pas que c'est un commentaire... sauf quand c'en est un. Le contexte tranche ; ne recopiez jamais l'invite elle-même.

Croire que which dit la vérité. Il ignore alias, fonctions et commandes internes : which echo répond /usr/bin/echo, alors que Bash utilisera sa commande interne. type -a.

Sécurité

Ne collez pas ce que vous n'avez pas lu. La forme curl https://exemple.fr/install.sh | sh (ou | sudo bash) télécharge un script et l'exécute immédiatement, sans que vous l'ayez vu, avec vos droits ou ceux de root. Le site peut être compromis, le téléchargement interrompu au milieu d'une ligne (et la moitié de la commande exécutée), ou le contenu différent de celui qu'un navigateur aurait affiché. Téléchargez dans un fichier, lisez-le, vérifiez sa somme de contrôle si l'éditeur en publie une, puis exécutez-le. Les copier-coller depuis une page web ont un piège de plus : le texte copié peut contenir des caractères invisibles ou un saut de ligne caché qui exécute la commande avant que vous ayez pu la relire. Collez d'abord dans un éditeur en cas de doute.

L'historique est un fichier. ~/.bash_history conserve en clair tout ce que vous tapez, y compris un mot de passe passé en argument (mysql -p'motdepasse', curl -u camille:motdepasse, une clé d'API). Ce fichier est lisible par vous et par root, copié dans les sauvegardes, parfois partagé quand un compte l'est. Règles : ne pas mettre de secret dans une ligne de commande ; à défaut, la faire précéder d'une espace avec HISTCONTROL contenant ignorespace ; si un secret y est passé, le changer, puis le retirer de l'historique (history -d numéro, puis history -w pour réécrire le fichier). Les arguments d'une commande sont en outre visibles par les autres utilisateurs de la machine pendant son exécution (ps, leçon 10).

L'invite comme garde-fou. Une invite qui affiche le nom de la machine (et, dans certaines équipes, une couleur différente pour la production) et # pour root évite une classe entière d'erreurs : la bonne commande sur la mauvaise machine.

sudo !! relance ce que vous n'avez peut-être pas relu. Bash affiche la commande avant de l'exécuter, mais ne demande pas confirmation. Prenez l'habitude de rappeler la commande (flèche haut), d'ajouter sudo au début (Ctrl+A), et de la relire.

En production

  • Les shells interactifs et les scripts ne sont pas le même usage. Ce que vous tapez à la main sur un serveur ne laisse qu'une trace partielle (l'historique d'un utilisateur, que l'on peut effacer). En production, les opérations répétées vont dans des scripts versionnés (cours Bash pour l'automatisation) ou des outils de gestion de configuration, et les interventions manuelles sont tracées autrement (journal de sudo, leçon 8 ; enregistrement des sessions sur un bastion, cours SSH).
  • Les alias personnels n'ont pas leur place dans une procédure. Une procédure d'exploitation (runbook) n'utilise que des commandes et options standard, écrites en entier (ls -alF, pas ll), pour fonctionner sur n'importe quel serveur et pour n'importe qui.
  • Options longues dans les scripts et la documentation. systemctl --no-pager status se relit mieux que sa forme courte ; à la main, les options courtes vont plus vite.
  • Chez Lyneko, les serveurs et les nœuds Kubernetes sont administrés le moins possible à la main : la plupart des diagnostics passent par les journaux et les métriques centralisés. Quand une session SSH est nécessaire, elle passe par un compte nominatif, jamais par un compte partagé, pour que l'historique et le journal de sudo disent qui a fait quoi.

Exercices

1. Classer des commandes (niveau 100). Pour chacune des commandes suivantes, dites si c'est un alias, un mot-clé, une commande interne ou un programme externe, et pour ces derniers, où il se trouve : cd, pwd, ls, ll, for, cat, history, type, ssh. Vérifiez avec une seule commande.

Solution

type -a cd pwd ls ll for cat history type ssh. Sur Ubuntu : cd, history et type sont internes ; pwd est interne et existe en programme (/usr/bin/pwd) ; ls est un alias (ls --color=auto) vers le programme /usr/bin/ls ; ll est un alias ; for est un mot-clé ; cat et ssh sont des programmes de /usr/bin. Les alias n'existent que dans un shell interactif qui a lu ~/.bashrc : dans un script, ll serait introuvable.

2. Trouver la bonne page (niveau 100). Sans moteur de recherche : (a) quelle commande change le mot de passe d'un utilisateur, et quelle page décrit le format du fichier qui contient les comptes ? (b) Quelle commande affiche le nombre de lignes d'un fichier ? (c) Que fait l'option -n de la commande interne echo ?

Solution

(a) man -f passwd : la commande est passwd(1), le format du fichier est décrit par man 5 passwd. (b) apropos "count" ou apropos lines mène à wc(1) ; wc -l fichier (leçon 5). (c) help echo : -n n'ajoute pas de saut de ligne final. man echo décrit le programme /usr/bin/echo, très proche mais pas identique : pour une commande interne, help fait foi.

3. Prévoir avant d'exécuter (niveau 100). Sans les lancer, écrivez ce qu'affichent ces commandes, puis vérifiez : echo "$HOME", echo '$HOME', echo \$HOME, echo "il a dit \"bonjour\"", echo 'l'\''ancien serveur'.

Solution

/home/camille (la variable est remplacée entre guillemets doubles) ; $HOME (rien n'est interprété entre guillemets simples) ; $HOME (le $ est échappé) ; il a dit "bonjour" (les guillemets doubles échappés à l'intérieur de guillemets doubles) ; l'ancien serveur. Dans le dernier cas, on ne peut pas mettre d'apostrophe dans des guillemets simples : on ferme les guillemets, on ajoute une apostrophe échappée \', et on les rouvre. Le shell colle les trois morceaux en un seul mot.

4. Le fichier piégé (niveau 100). Dans un répertoire d'essai, créez un fichier nommé --help et un fichier nommé mon rapport.txt. Affichez-les avec ls -l, puis supprimez-les un par un. Notez chaque erreur rencontrée et sa cause.

Solution

touch -- --help "mon rapport.txt" (sans --, touch --help affiche l'aide ; sans guillemets, on crée deux fichiers mon et rapport.txt). ls -l les montre ; ls -l --help afficherait l'aide de ls. Suppression : rm -- --help ou rm ./--help, et rm "mon rapport.txt" ou rm mon\ rapport.txt. La complétion par Tab sur mon ajoute d'elle-même l'échappement nécessaire.

Récapitulatif

  • Le terminal transporte vos frappes, le shell les interprète ; par SSH, un pseudo-terminal relie les deux. Le shell des comptes Ubuntu est Bash ; /bin/sh y est Dash.
  • Une commande se compose d'un nom, d'options (courtes groupables, longues avec --) et d'arguments ; -- termine les options.
  • Le shell cherche successivement un alias, un mot-clé, une fonction, une commande interne, puis un programme dans PATH. type -a le révèle ; which ne voit que PATH.
  • L'aide : --help, man (sections : 1 commandes, 5 fichiers, 8 administration), apropos, help pour les internes.
  • Historique (Ctrl+R, history, !!) et complétion (Tab) ; HISTCONTROL=ignorespace exclut les lignes qui commencent par une espace.
  • Guillemets doubles : variables remplacées, espaces gardées ; simples : tout est littéral ; \ protège un caractère.
  • Pour un programme externe, le shell duplique son processus puis le remplace par le programme (clone, execve) ; le programme ne reçoit qu'un tableau de mots, après expansions.
  • Ne collez pas ce que vous n'avez pas lu ; ne tapez pas de secret dans une commande.

Pour aller plus loin

  • Le GNU Bash Reference Manual, chapitre 3 (Basic Shell Features), pour la description exacte des expansions et des guillemets.
  • Les règles de syntaxe des utilitaires de POSIX (chapitre 12 des définitions de base), courtes, qui expliquent pourquoi tant de commandes se ressemblent.
  • man man et man 7 man-pages, pour lire le manuel comme ses auteurs l'écrivent.
  • La leçon suivante, qui part du répertoire où vous a placé la connexion et parcourt l'arborescence du serveur.
Voir ma constellation →

Sources