Administration Linux
Les bases du shell Bash : commandes, redirections, pipes, variables et configuration
Débutant10 min de lectureMis à jour
Cette page a été traduite de l'anglais avec l'aide de l'IA, puis vérifiée par rapport à l'original. Lire la version anglaise
Ce guide explique ce qui se passe réellement entre l'appui sur Entrée et l'exécution d'un programme : recherche de la commande, quoting, développements et les trois flux standard. Vous apprendrez à rediriger la sortie et les erreurs, à construire des pipelines, à utiliser l'historique et les raccourcis d'édition qui rendent le shell rapide, à gérer les variables d'environnement et le PATH, et à configurer Bash via .bashrc et .profile sans casser votre ouverture de session.
Ce que vous allez apprendre
- Expliquer comment Bash analyse une ligne de commande et trouve le programme à exécuter
- Rediriger la sortie standard et l'erreur standard vers des fichiers et relier des commandes avec des pipes
- Utiliser correctement le quoting et le globbing pour que les espaces et les caractères spéciaux ne vous réservent pas de surprises
- Définir, exporter et inspecter les variables du shell et d'environnement, y compris PATH
- Configurer alias et environnement dans le bon fichier de démarrage et exploiter efficacement les pages man
Un shell est un programme qui lit des lignes de texte, détermine quels programmes lancer et avec quels arguments, les exécute et vous montre le résultat. Bash (le « Bourne-Again SHell ») est le shell interactif par défaut de presque toutes les distributions Linux, et l'interpréteur que la plupart des scripts d'administration présupposent encore. Comprendre ce que Bash fait d'une ligne de commande avant d'exécuter quoi que ce soit — développements, quoting, redirections — c'est ce qui distingue ceux qui tapent des commandes de ceux qui savent prédire ce qu'une commande va faire.
Ce qui se passe quand vous appuyez sur Entrée
Prenez une ligne comme grep -i error /var/log/*.log > errors.txt. Bash la traite par étapes :
- Il découpe la ligne en mots sur les espaces non protégés.
- Il effectue les développements :
~devient votre répertoire personnel,$VARest remplacé par sa valeur,$(cmd)par la sortie d'une commande,{a,b}para b, et les motifs glob comme*.logpar les noms de fichiers correspondants. - Il met en place les redirections (
>,<,|) et les retire de la liste des arguments. - Il résout le premier mot : un alias, une fonction du shell, une commande interne (builtin :
cd,echo,export…) ou un programme externe trouvé en parcourant les répertoires dePATH, dans cet ordre. - Il crée un processus enfant (fork), y applique les redirections et exécute le programme avec les mots restants comme arguments.
Le programme ne voit jamais le *, le > ni les guillemets ; il reçoit une liste de chaînes. grep ne sait pas que *.log était un motif — il reçoit simplement /var/log/syslog /var/log/kern.log …. Voilà pourquoi le quoting compte autant, et pourquoi tous les programmes Linux gèrent les jokers de la même façon : aucun d'eux ne s'en occupe, c'est le shell qui le fait.
type indique à quelle catégorie appartient un nom, et command -v affiche le chemin d'une commande externe :
$ type cd ls ll
cd is a shell builtin
ls is /usr/bin/ls
ll is aliased to `ls -alF'
$ echo "$PATH"
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PATH est parcouru de gauche à droite, et la première correspondance l'emporte. Le répertoire courant n'y figure volontairement pas : un programme de votre répertoire de travail doit donc être lancé avec ./program. Cela empêche un ls malveillant déposé dans /tmp de détourner vos commandes.
Anatomie d'une commande
La plupart des commandes suivent la forme command [options] [arguments]. Les options existent en forme courte (-l), courte groupée (-la = -l -a) et longue (--all) ; certaines prennent une valeur (-n 5, --lines=5). Les arguments sont généralement des noms de fichiers. Un -- seul marque la fin des options : rm -- -rf supprime ainsi un fichier nommé littéralement -rf.
Le manuel est intégré. man ls ouvre la page de ls ; à l'intérieur, /pattern lance une recherche, n passe à l'occurrence suivante et q quitte. Les pages sont regroupées en sections numérotées — 1 pour les commandes, 5 pour les formats de fichiers, 8 pour l'administration — et le numéro lève les ambiguïtés : man 5 passwd documente le format de fichier, man 1 passwd la commande. man -k keyword (ou apropos) cherche dans les résumés des pages quand vous ne connaissez pas le nom de la commande. La plupart des programmes affichent aussi un résumé avec --help.
Les trois flux standard
Chaque processus démarre avec trois descripteurs de fichier ouverts : l'entrée standard (fd 0), la sortie standard (fd 1) et l'erreur standard (fd 2). Dans un terminal, les trois pointent vers le terminal, d'où le mélange des sorties et des messages d'erreur à l'écran. La redirection les fait pointer ailleurs avant le démarrage du programme.
| Syntaxe | Effet |
|---|---|
cmd > file | stdout vers file, après l'avoir vidé |
cmd >> file | stdout ajouté à la fin de file |
cmd 2> file | stderr vers file |
cmd > file 2>&1 ou cmd &> file | les deux flux vers file |
cmd 2> /dev/null | ignorer les erreurs |
cmd < file | stdin lu depuis file |
cmd <<< "text" | stdin depuis une chaîne (une « here string ») |
cmd1 | cmd2 | le stdout de cmd1 devient le stdin de cmd2 |
Comme le programme ne voit que des descripteurs, il ne peut pas savoir s'il écrit vers un terminal, un fichier ou un autre processus. C'est ce choix de conception qui rend les pipelines possibles : de petits programmes qui lisent stdin et écrivent sur stdout peuvent s'enchaîner à volonté.
# Compter les adresses IP distinctes qui ont sollicité le serveur, les plus actives en premier
cut -d' ' -f1 /var/log/apache2/access.log | sort | uniq -c | sort -rn | head
tee copie son entrée dans un fichier et sur stdout, ce qui permet de garder une trace tout en suivant l'exécution : ./build.sh 2>&1 | tee build.log. xargs transforme des lignes d'entrée en arguments pour une autre commande : find . -name '*.tmp' -print0 | xargs -0 rm (le couple -print0/-0 préserve les noms de fichiers contenant des espaces).
Deux commandes internes complètent le tableau. set -o noclobber empêche > d'écraser un fichier existant (>| force l'écriture). Et exec > logfile redirige la sortie standard du shell lui-même pour le reste d'un script.
Quoting et développements
Le quoting détermine quels développements ont lieu.
- Les guillemets simples
'…'neutralisent tout.'$HOME'correspond littéralement aux cinq caractères$HOME. - Les guillemets doubles
"…"neutralisent le globbing et le découpage en mots, mais effectuent toujours les développements$var,$(cmd)et$((math)). - Un antislash échappe le caractère qui le suit, et lui seul.
Conséquence pratique : un $file sans guillemets contenant my report.txt devient deux arguments après le découpage en mots. "$file" reste un seul argument. Prenez l'habitude de mettre chaque développement de variable entre guillemets doubles, sauf si vous voulez précisément le découpage.
name="Ada Lovelace"
echo Hello $name # echo reçoit 3 arguments : Hello, Ada, Lovelace
echo "Hello $name" # echo reçoit 1 argument
echo 'Hello $name' # affiche Hello $name
echo "Today is $(date +%A)"
echo "Free memory: $(( $(grep MemAvailable /proc/meminfo | tr -s ' ' | cut -d' ' -f2) / 1024 )) MB"
Les motifs glob correspondent à des noms de fichiers : * pour une suite quelconque de caractères, ? pour un caractère, [a-z] pour un caractère d'un ensemble, [!0-9] pour un caractère hors de l'ensemble. shopt -s globstar active ** pour la correspondance récursive (**/*.java). Une particularité importante : par défaut, un motif qui ne correspond à rien est transmis tel quel. Ainsi rm *.bak, dans un répertoire sans fichier .bak, tente de supprimer un fichier nommé *.bak et échoue. shopt -s nullglob fait disparaître les motifs sans correspondance, ce que les scripts veulent généralement.
Le développement des accolades n'est pas du globbing — il génère des chaînes, que les fichiers existent ou non : mkdir -p project/{src,test,docs}, cp config.yml{,.bak}, echo {01..12}.
Variables et environnement
L'affectation s'écrit sans espace autour du = : retries=3. La lecture se fait avec $retries ou, plus sûr, ${retries}. Bash fournit de nombreuses variables automatiques : $? est le code de retour de la dernière commande, $$ le PID du shell, puis $HOME, $USER, $PWD, $RANDOM, $SECONDS.
Une simple affectation crée une variable du shell, propre à ce processus. export la promeut dans l'environnement, le bloc de chaînes NAME=value transmis à chaque processus enfant. env et printenv listent l'environnement ; set liste tout, y compris les variables du shell et les fonctions.
JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
export JAVA_HOME # désormais visible des programmes que vous lancez
export PATH="$JAVA_HOME/bin:$PATH" # en tête : trouvé avant le java du système
LANG=C sort names.txt # ponctuel : uniquement pour cette commande
unset JAVA_HOME
Le développement de paramètres offre des valeurs par défaut et des transformations qui épargnent beaucoup de code :
echo "${EDITOR:-nano}" # la valeur, ou "nano" si non définie/vide
: "${DB_HOST:?must be set}" # abandonne avec un message si non définie
f=/var/log/app/server.log
echo "${f##*/}" # server.log (retire le plus long préfixe correspondant à */)
echo "${f%.log}" # /var/log/app/server (retire le suffixe)
echo "${#f}" # longueur de la chaîne
Code de retour et enchaînement de commandes
Chaque commande se termine par un code de retour entier : 0 signifie succès, toute autre valeur un échec (la signification des valeurs non nulles dépend du programme). Bash l'expose via $? et s'en sert pour le contrôle de flux. Trois opérateurs enchaînent les commandes :
make && sudo make install # installer seulement si la compilation a réussi
ping -c1 db01 || echo "db01 unreachable"
cd /tmp; ls # exécuter les deux quoi qu'il arrive
grep renvoie 1 quand il ne trouve rien : grep -q pattern file && do_something est donc idiomatique. true et false n'existent que pour produire des codes de retour.
Aller plus vite : historique et édition de ligne
Bash conserve un historique des commandes (history, enregistré dans ~/.bash_history à la sortie). Les rappeler est plus rapide que les retaper :
Ctrl-Rpuis tapez votre recherche — recherche incrémentale vers l'arrière ; appuyez de nouveau surCtrl-Rpour des correspondances plus anciennes, Entrée pour exécuter, Échap pour modifier.!!répète la dernière commande (sudo !!en est l'usage classique) ;!$est le dernier argument de la commande précédente.Alt-.insère interactivement le dernier argument de la commande précédente.
L'éditeur de ligne utilise par défaut les raccourcis Emacs : Ctrl-A/Ctrl-E vont au début/à la fin, Ctrl-W supprime le mot précédent, Ctrl-U efface jusqu'au début de la ligne, Ctrl-K jusqu'à la fin, Ctrl-L efface l'écran, et Tab complète les noms de fichiers, les noms de commandes et — avec le paquet bash-completion — les options et arguments de centaines d'outils. Appuyez deux fois sur Tab pour lister les candidats.
Ctrl-C interrompt la commande en cours ; Ctrl-D sur une invite vide envoie une fin de fichier et vous déconnecte ; Ctrl-Z suspend la tâche au premier plan (traité avec le reste du contrôle des tâches dans Processus et systemd).
Alias, fonctions et fichiers de démarrage
Un alias est un raccourci textuel développé lorsqu'il est le premier mot d'une commande : alias ll='ls -alF', alias gs='git status'. Un alias ne peut pas prendre d'arguments au milieu ; pour cela, définissez une fonction :
mkcd() { mkdir -p -- "$1" && cd -- "$1"; }
Bash lit des fichiers différents selon la manière dont il a été lancé, d'où la plainte si fréquente : « je l'ai ajouté dans .bashrc et ça ne marche pas ».
| Type de shell | Fichiers lus | Exemple |
|---|---|---|
| Interactif de connexion | /etc/profile, puis le premier trouvé parmi ~/.bash_profile, ~/.bash_login, ~/.profile | Session SSH, connexion sur la console |
| Interactif sans connexion | /etc/bash.bashrc (Debian) ou /etc/bashrc (RHEL), puis ~/.bashrc | Nouvelle fenêtre de terminal, bash tapé à l'invite |
| Non interactif | Rien, sauf si BASH_ENV est défini | Scripts, cron, ssh host command |
L'organisation habituelle : placez les variables d'environnement (PATH, JAVA_HOME, EDITOR) dans ~/.profile, pour qu'elles soient définies une seule fois à la connexion et héritées par tout le reste, et mettez les alias, fonctions, réglages de l'invite et options shopt dans ~/.bashrc, car ils doivent être redéfinis dans chaque shell interactif. Sur Debian et Ubuntu, le ~/.profile par défaut charge ~/.bashrc : les deux s'appliquent donc aux connexions SSH. Les réglages communs à tous les utilisateurs vont dans un nouveau fichier sous /etc/profile.d/.
Après modification, appliquez les changements au shell courant avec source ~/.bashrc (ou . ~/.bashrc). Lancer bash ~/.bashrc l'exécuterait dans un enfant qui se termine aussitôt, sans rien changer.
Trouver de l'aide sans quitter le terminal
Au-delà de man et --help : help cd documente une commande interne (la page man de Bash fait plus de 100 pages, help va droit au but) ; info coreutils donne accès aux manuels GNU complets ; whatis cmd affiche le résumé en une ligne ; et /usr/share/doc/<package>/ contient les README, exemples et journaux de modifications installés avec chaque paquet. Quand la sortie d'une commande est longue, redirigez-la vers less avec un pipe et cherchez avec /.
Erreurs courantes
Des espaces autour du =. count = 3 exécute une commande nommée count avec deux arguments. Une affectation ne comporte pas d'espaces.
Des variables sans guillemets contenant des espaces ou des globs. rm $file avec file="report *.txt" supprime tous les fichiers .txt. Mettez-la entre guillemets.
Croire que .bashrc s'applique à cron, à systemd ou aux scripts. Les shells non interactifs l'ignorent. Définissez PATH et les autres variables dans le script ou dans l'unité.
L'ordre des redirections. cmd 2>&1 > file affiche toujours les erreurs dans le terminal. Placez > file en premier, ou utilisez &> file.
Lancer avec sh des scripts écrits pour Bash. sh script.sh utilise Dash sur Debian et Ubuntu et échoue sur [[, les tableaux et local -n. Utilisez le shebang et chmod +x, puis lancez ./script.sh.
Écrire cat file | grep x. C'est inoffensif, mais grep x file est plus simple et permet à grep d'afficher les noms de fichiers quand il en reçoit plusieurs. Réservez cat à la concaténation.
Écraser le fichier que vous êtes en train de lire. sort names.txt > names.txt tronque le fichier avant même que sort ne le lise : il se retrouve vide. Écrivez dans un fichier temporaire puis renommez-le, ou utilisez sponge du paquet moreutils.
Points clés à retenir
- Bash développe la ligne de commande — variables, globs, accolades, substitution de commande — avant l'exécution du programme ; les programmes reçoivent de simples chaînes d'arguments.
- L'ordre de résolution d'une commande est : alias, fonction, commande interne, puis recherche dans
PATH;typeindique lequel s'applique. - Les trois flux standard et les opérateurs de redirection (
>,>>,2>,2>&1,<,|) sont ce qui rend les petits outils composables. - Mettez les développements de variables entre guillemets doubles, et le texte littéral contenant
$ou des antislashs entre guillemets simples. exportfait passer une variable dans l'environnement des processus enfants ; les variables non exportées restent dans le shell.- L'environnement va dans
~/.profile, les alias et réglages de l'invite dans~/.bashrc; les shells non interactifs ne lisent ni l'un ni l'autre. - Un code de retour de 0 signifie succès, et
&&,||et$?permettent d'agir en conséquence.
Sources et lectures complémentaires
Questions fréquentes
- Quelle différence entre une variable du shell et une variable d'environnement ?
- Une variable du shell n'existe que dans le processus shell courant. L'exporter (export NOM=valeur) la copie dans le bloc d'environnement transmis à chaque processus enfant lancé par le shell. Des programmes comme java, python ou curl ne voient que les variables exportées. Les variables définies dans un script ne sont jamais visibles du shell parent, car un enfant ne peut pas modifier la mémoire de son parent.
- Pourquoi mon alias ou ma modification du PATH fonctionne dans un terminal, mais pas dans cron ou un service systemd ?
- Les alias et le .bashrc ne sont lus que par les shells interactifs. Les tâches cron et les services tournent sans terminal et avec un environnement minimal : ils ne voient ni vos alias ni les ajouts au PATH faits dans .bashrc. Utilisez des chemins absolus dans les scripts, définissez PATH explicitement en tête de script, ou placez l'environnement dans l'unité du service ou dans la crontab elle-même.
- Quand utiliser des guillemets simples et quand des guillemets doubles ?
- Les guillemets simples sont littéraux : rien n'y est développé, ce qui en fait le bon choix pour les expressions régulières, les programmes awk et tout ce qui contient des $ ou des antislashs à conserver. Les guillemets doubles gardent les espaces ensemble mais développent toujours les $variables et les $(commandes), ce que vous voulez pour passer une variable comme un seul argument. En pratique : mettez chaque référence de variable entre guillemets doubles, et le texte fixe contenant des caractères spéciaux entre guillemets simples.
- Que signifie 2>&1 et pourquoi l'ordre compte-t-il ?
- 2>&1 fait du descripteur 2 (erreur standard) une copie de l'endroit où pointe à cet instant le descripteur 1 (sortie standard). Les redirections s'appliquent de gauche à droite : cmd > log 2>&1 envoie les deux flux dans le fichier, alors que cmd 2>&1 > log copie d'abord stderr vers le terminal et ne pointe stdout vers le fichier qu'ensuite. Bash accepte aussi le raccourci &> log.