Environnement et configuration du shell#

Hide code cell source

import matplotlib.pyplot as plt
import matplotlib.patches as patches
import numpy as np
import seaborn as sns

sns.set_theme(style="whitegrid", palette="muted", font_scale=1.1)

L’environnement shell est l’ensemble des variables, alias, fonctions et paramètres qui définissent le comportement de votre shell et de tous les processus qu’il lance. Comprendre comment Bash initialise cet environnement — quels fichiers il lit, dans quel ordre, et dans quelles circonstances — est indispensable pour configurer un environnement de travail cohérent, diagnostiquer des comportements inattendus, et automatiser des tâches qui dépendent de l’environnement.

Ce chapitre couvre les fichiers de démarrage de Bash, la définition d’alias et de fonctions persistantes, le fonctionnement du PATH, les principales variables d’environnement système, l’outil tmux pour les sessions persistantes, et direnv pour les environnements par répertoire.

Fichiers de configuration de Bash#

Bash distingue plusieurs types de shells selon leur mode d’invocation, et chaque type lit des fichiers de configuration différents.

Définition 49 (Shell de login vs shell interactif)

Bash reconnaît deux dimensions indépendantes pour son mode d’invocation :

  • Shell de login (login shell) : shell lancé lors d’une ouverture de session (connexion SSH, TTY virtuel, su -, etc.). Il lit les fichiers de configuration globaux et personnels de démarrage.

  • Shell non-login : shell lancé dans un terminal existant (ouvrir un nouveau terminal dans GNOME, lancer bash depuis un script, etc.).

  • Shell interactif : shell qui lit et écrit sur un terminal, attend des commandes de l’utilisateur.

  • Shell non-interactif : shell lancé pour exécuter un script sans intervention humaine.

Ces deux dimensions se combinent : on peut avoir un shell interactif non-login (terminal dans une session graphique), un shell de login non-interactif (rare), etc.

Fichiers lus selon le type de shell#

Shell interactif de LOGIN#

Un shell de login lit les fichiers suivants, dans l’ordre :

  1. /etc/profile — configuration système globale

  2. ~/.bash_profile — si ce fichier existe, les suivants ne sont pas lus

  3. ~/.bash_login — lu si ~/.bash_profile n’existe pas

  4. ~/.profile — lu si ni ~/.bash_profile ni ~/.bash_login n’existent

À la fermeture d’un shell de login, Bash lit ~/.bash_logout.

Shell interactif NON-LOGIN#

Un shell interactif non-login lit uniquement :

  1. /etc/bash.bashrc — configuration système globale

  2. ~/.bashrc — configuration personnelle

Remarque 40

La convention standard sur Ubuntu/Debian est que ~/.bash_profile appelle ~/.bashrc explicitement :

# Contenu typique de ~/.bash_profile
if [[ -f ~/.bashrc ]]; then
    source ~/.bashrc
fi

Cela garantit que la configuration définie dans ~/.bashrc s’applique aussi bien aux shells de login qu’aux shells non-login interactifs. La plupart des personnalisations (alias, fonctions, apparence du prompt, PATH) doivent donc être placées dans ~/.bashrc.


### Rôle de chaque fichier

#### `/etc/profile`

Exécuté pour tous les utilisateurs lors d'un shell de login. Sur Debian/Ubuntu, il exécute aussi les scripts dans `/etc/profile.d/` :

```bash
# Exemple de /etc/profile
# set PATH so it includes user's private bin if it exists
if [ -d "$HOME/bin" ] ; then
    PATH="$HOME/bin:$PATH"
fi

# Exécuter les scripts dans /etc/profile.d/
if [ -d /etc/profile.d ]; then
  for i in /etc/profile.d/*.sh; do
    if [ -r $i ]; then
      . $i
    fi
  done
fi

~/.bash_profile ou ~/.profile#

Configuration personnelle de login. À placer ici : les variables d’environnement exportées vers les processus enfants, le PATH pour les outils utilisateur (~/.local/bin, etc.).

# ~/.bash_profile
# Charger .bashrc si interactif
[[ -f ~/.bashrc ]] && source ~/.bashrc

# Variables d'environnement permanentes
export EDITOR="vim"
export VISUAL="vim"
export PAGER="less"
export LANG="fr_FR.UTF-8"

# Outils installés par l'utilisateur
export PATH="$HOME/.local/bin:$HOME/bin:$PATH"

# NVM, rbenv, pyenv, etc.
export NVM_DIR="$HOME/.nvm"
[[ -s "$NVM_DIR/nvm.sh" ]] && source "$NVM_DIR/nvm.sh"

~/.bashrc#

Le fichier de configuration le plus important pour l’usage quotidien. Il est lu à chaque ouverture d’un terminal interactif.

# ~/.bashrc — structure typique

# Sortir immédiatement si shell non interactif
case $- in
    *i*) ;;      # Shell interactif : continuer
      *) return;; # Non interactif : ne rien faire
esac

# --- Historique ---
HISTSIZE=10000
HISTFILESIZE=20000
HISTCONTROL=ignoreboth    # ignorer les doublons et les lignes commençant par espace
shopt -s histappend       # Ajouter à l'historique, ne pas écraser

# --- Options shell ---
shopt -s checkwinsize     # Mettre à jour LINES et COLUMNS après chaque commande
shopt -s globstar         # Activer ** pour la récursion
shopt -s cdspell          # Corriger les fautes de frappe dans cd

# --- Prompt ---
PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '

# --- Aliases ---
alias ll='ls -alF'
alias la='ls -A'
alias l='ls -CF'

# --- PATH ---
export PATH="$HOME/.local/bin:$PATH"

# --- Chargement des fichiers supplémentaires ---
[[ -f ~/.bash_aliases ]] && source ~/.bash_aliases
[[ -f ~/.bash_functions ]] && source ~/.bash_functions

~/.bash_logout#

Exécuté à la fermeture d’un shell de login. Utile pour des nettoyages.

# ~/.bash_logout — exemples d'utilisation
clear                    # Effacer l'écran à la déconnexion
history -w               # Sauvegarder l'historique avant de quitter
echo "Session fermée le $(date)" >> ~/.sessions.log

Alias#

Un alias est un raccourci de commande : il remplace une chaîne de caractères par une autre avant l’interprétation de la ligne de commande.

# Définir un alias
alias ll='ls -alF --color=auto'
alias la='ls -A'
alias grep='grep --color=auto'
alias ..='cd ..'
alias ...='cd ../..'
alias ....='cd ../../..'

# Alias avec arguments ? Impossible directement — utiliser une fonction
alias df='df -h'
alias du='du -h'
alias free='free -h'

# Git
alias gs='git status'
alias ga='git add'
alias gc='git commit'
alias gp='git push'
alias gl='git log --oneline --graph --decorate'
alias gd='git diff'

# Sécurité : demander confirmation avant les opérations destructives
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'

# Voir tous les alias définis
alias

# Voir la définition d'un alias spécifique
alias ll

# Supprimer un alias (pour la session courante)
unalias ll

# Appeler la commande originale sans l'alias (utile quand on a aliasé rm)
\rm fichier.txt      # Le \ désactive l'alias
command rm fichier.txt   # Alternative explicite

Persistance des alias#

Un alias défini dans le terminal ne survit pas à la fermeture de la session. Pour le rendre permanent, il faut l’ajouter à ~/.bashrc ou à un fichier dédié ~/.bash_aliases sourcé par ~/.bashrc.

# Ajouter un alias permanent
echo "alias ll='ls -alF'" >> ~/.bash_aliases

# Recharger la configuration sans fermer le terminal
source ~/.bashrc
# Ou forme courte :
. ~/.bashrc

Fonctions permanentes dans .bashrc#

Les fonctions Bash permettent des raccourcis plus puissants que les alias car elles acceptent des arguments, des structures de contrôle et plusieurs commandes.

# Exemples de fonctions utiles à mettre dans ~/.bashrc

# Navigation rapide vers le projet courant
proj() {
    local base="$HOME/projets"
    if [[ -z "$1" ]]; then
        cd "$base"
    else
        cd "$base/$1"
    fi
}

# mkdir puis cd dans le nouveau répertoire
mkcd() {
    mkdir -p "$1" && cd "$1"
}

# Extraire n'importe quelle archive
extraire() {
    if [[ -f "$1" ]]; then
        case "$1" in
            *.tar.bz2)  tar -xjf "$1"  ;;
            *.tar.gz)   tar -xzf "$1"  ;;
            *.tar.xz)   tar -xJf "$1"  ;;
            *.bz2)      bunzip2 "$1"   ;;
            *.gz)       gunzip "$1"    ;;
            *.zip)      unzip "$1"     ;;
            *.7z)       7z x "$1"      ;;
            *.rar)      unrar x "$1"   ;;
            *)          echo "Format non reconnu : $1" >&2; return 1 ;;
        esac
    else
        echo "'$1' n'est pas un fichier." >&2
        return 1
    fi
}

# Chercher dans l'historique
h() { history | grep --color=auto "$*"; }

# Afficher la météo (via wttr.in)
meteo() { curl -s "wttr.in/${1:-Paris}?lang=fr"; }

# Sauvegarder un fichier avec horodatage
sauvegarder() {
    cp "$1" "${1}.$(date +%Y%m%d_%H%M%S).bak"
    echo "Sauvegarde créée : ${1}.$(date +%Y%m%d_%H%M%S).bak"
}

Remarque 41

La différence entre un alias et une fonction est que les alias ne peuvent pas traiter d’arguments : alias sauve='cp $1 $1.bak' ne fonctionnera pas car $1 est développé lors de la définition de l’alias, pas lors de son appel. Pour tout raccourci nécessitant des arguments, des conditions ou plusieurs commandes, il faut utiliser une fonction. Une règle pratique : alias pour les options fixes d’une commande existante, fonction pour toute logique.

Le PATH : résolution des commandes#

Quand on tape vim dans le terminal, Bash doit retrouver l’exécutable correspondant. Il parcourt pour cela les répertoires listés dans la variable PATH, de gauche à droite, et utilise le premier exécutable vim trouvé.

Définition 50 (Variable PATH)

La variable d’environnement PATH est une liste de répertoires séparés par des deux-points (:) dans lesquels Bash cherche les commandes à exécuter. Quand une commande ne contient pas de /, Bash parcourt les répertoires du PATH dans l’ordre et exécute le premier fichier exécutable correspondant trouvé. Si aucun n’est trouvé, Bash retourne l’erreur command not found.

# Afficher le PATH actuel
echo $PATH
# /home/alice/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:...

# Trouver l'emplacement d'une commande
which vim          # /usr/bin/vim
which python3      # /usr/bin/python3
type vim           # vim is /usr/bin/vim  (aussi : alias, fonctions, builtins)
type ll            # ll is aliased to 'ls -alF'
command -v vim     # /usr/bin/vim  (portable, retourne 1 si absent)

# Ajouter un répertoire au début du PATH (priorité max)
export PATH="$HOME/.local/bin:$PATH"

# Ajouter à la fin (priorité minimale)
export PATH="$PATH:/opt/mon_outil/bin"

# Ajouter plusieurs répertoires
export PATH="$HOME/.local/bin:$HOME/bin:/opt/outil/bin:$PATH"

# Rendre la modification permanente (dans ~/.bashrc ou ~/.bash_profile)
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc

# Conventions de répertoires dans le PATH
# /bin et /usr/bin    : commandes système essentielles
# /sbin et /usr/sbin  : commandes d'administration
# /usr/local/bin      : logiciels compilés localement
# ~/.local/bin        : commandes personnelles (PEP 517, pipx, etc.)
# ~/bin               : scripts personnels (créer et ajouter si besoin)

Gestion des binaires utilisateur#

# Créer un répertoire de scripts personnels
mkdir -p ~/.local/bin

# Exemple : créer un script personnel
cat > ~/.local/bin/bienvenue << 'EOF'
#!/usr/bin/env bash
echo "Bonjour, $USER ! Il est $(date +%H:%M)."
EOF
chmod +x ~/.local/bin/bienvenue

# Vérifier que le répertoire est dans le PATH
echo $PATH | grep -o "$HOME/.local/bin" && echo "OK" || echo "À ajouter au PATH"

# Exécuter
bienvenue

Variables d’environnement système importantes#

Les variables d’environnement sont des variables exportées, accessibles non seulement dans le shell courant mais aussi dans tous les processus enfants qu’il lance.

Définition 51 (Variable d’environnement)

Une variable d’environnement est une variable shell marquée pour être exportée (export) : elle est transmise automatiquement à tout processus enfant créé par le shell. Les commandes, éditeurs, compilateurs et programmes utilisent ces variables pour adapter leur comportement sans nécessiter de paramètres explicites.

# Afficher toutes les variables d'environnement
env
printenv
export -p    # Afficher les variables exportées avec leur valeur

# Afficher une variable spécifique
echo $HOME
printenv HOME

# Variables essentielles
echo $HOME      # /home/alice — répertoire personnel
echo $USER      # alice — nom de l'utilisateur courant
echo $LOGNAME   # alice — identique à USER dans la plupart des cas
echo $SHELL     # /bin/bash — shell courant
echo $UID       # 1000 — identifiant numérique de l'utilisateur
echo $HOSTNAME  # machine.local — nom de la machine
echo $PWD       # /home/alice/projets — répertoire courant
echo $OLDPWD    # répertoire précédent (utilisé par cd -)
echo $LANG      # fr_FR.UTF-8 — locale système
echo $LC_ALL    # (peut écraser LANG)
echo $TERM      # xterm-256color — type de terminal
echo $COLORTERM # truecolor — support des couleurs
echo $EDITOR    # vim — éditeur de texte en ligne de commande
echo $VISUAL    # vim — éditeur visuel (pour git commit, cron, etc.)
echo $PAGER     # less — paginateur (man, git log, etc.)
echo $PS1       # Chaîne du prompt principal
echo $PS2       # Prompt secondaire (lignes continuées)
echo $IFS       # Séparateur de champs interne (' \t\n' par défaut)
echo $RANDOM    # Nombre aléatoire (0-32767) — re-évalué à chaque lecture
echo $LINENO    # Numéro de ligne dans le script courant
echo $SECONDS   # Secondes depuis le démarrage du shell

Variables de la session#

# Définir et exporter une variable
export MA_VARIABLE="valeur"

# Définir une variable pour une seule commande (sans export permanent)
LANG=en_US.UTF-8 sort fichier.txt

# Supprimer une variable
unset MA_VARIABLE

# Variables importantes pour les outils de développement
export GOPATH="$HOME/go"
export GOBIN="$GOPATH/bin"
export CARGO_HOME="$HOME/.cargo"
export JAVA_HOME="/usr/lib/jvm/java-21-openjdk-amd64"
export PYTHONPATH="$HOME/lib/python"
export NODE_PATH="$HOME/.nvm/versions/node/v20/lib/node_modules"

# Variables de proxy (pour les environnements d'entreprise)
export http_proxy="http://proxy.entreprise.fr:8080"
export https_proxy="http://proxy.entreprise.fr:8080"
export no_proxy="localhost,127.0.0.1,.local,.entreprise.fr"

tmux : sessions persistantes et multiplexage de terminal#

tmux (Terminal MUltiplexer) est un outil indispensable pour quiconque travaille sur des serveurs distants. Il permet de créer des sessions persistantes qui survivent à la déconnexion SSH, de diviser un terminal en plusieurs fenêtres et panneaux, et de reprendre le travail là où on l’avait laissé.

Définition 52 (tmux)

tmux est un multiplexeur de terminal qui fonctionne selon une hiérarchie à trois niveaux :

  • Session : contexte de travail indépendant du terminal. Une session peut contenir plusieurs fenêtres. Les sessions persistent même quand aucun terminal n’y est attaché.

  • Fenêtre (window) : équivalent d’un onglet. Chaque fenêtre occupe la totalité de l’écran et peut contenir un ou plusieurs panneaux.

  • Panneau (pane) : subdivision d’une fenêtre. Chaque panneau exécute un shell ou une commande indépendante.

Commandes essentielles#

# Démarrer une nouvelle session
tmux
tmux new-session
tmux new -s nom_session    # Avec un nom explicite (recommandé)

# Lister les sessions actives
tmux list-sessions
tmux ls

# Se rattacher à une session existante
tmux attach
tmux attach -t nom_session
tmux a -t nom_session      # Forme courte

# Se détacher d'une session (la session continue en arrière-plan)
# Ctrl+b puis d  (d = detach)

# Tuer une session
tmux kill-session -t nom_session
tmux kill-server            # Tuer toutes les sessions

Le préfixe tmux#

Toutes les commandes tmux commencent par un préfixe : Ctrl+b par défaut. On appuie sur Ctrl+b, on relâche, puis on tape la touche de commande.

Gestion des sessions :

Raccourci

Action

Ctrl+b d

Détacher de la session (session reste active)

Ctrl+b $

Renommer la session courante

Ctrl+b s

Lister et changer de session

Ctrl+b ( / )

Session précédente / suivante

Gestion des fenêtres :

Raccourci

Action

Ctrl+b c

Créer une nouvelle fenêtre

Ctrl+b n

Fenêtre suivante

Ctrl+b p

Fenêtre précédente

Ctrl+b 0-9

Aller à la fenêtre numérotée

Ctrl+b ,

Renommer la fenêtre courante

Ctrl+b w

Lister toutes les fenêtres

Ctrl+b &

Fermer la fenêtre courante

Gestion des panneaux :

Raccourci

Action

Ctrl+b %

Diviser horizontalement (deux panneaux côte à côte)

Ctrl+b "

Diviser verticalement (deux panneaux haut/bas)

Ctrl+b ←↑↓→

Naviguer entre les panneaux

Ctrl+b Ctrl+←↑↓→

Redimensionner le panneau

Ctrl+b z

Agrandir/réduire un panneau en plein écran

Ctrl+b x

Fermer le panneau courant

Ctrl+b { / }

Déplacer le panneau

Ctrl+b q

Afficher les numéros de panneaux

Copie et défilement :

Raccourci

Action

Ctrl+b [

Mode copie (défilement dans l’historique)

Ctrl+b ]

Coller le tampon tmux

Ctrl+b PageUp

Remonter dans l’historique

q (en mode copie)

Quitter le mode copie

Le fichier ~/.tmux.conf#

# ~/.tmux.conf — configuration personnalisée de tmux

# Changer le préfixe de Ctrl+b à Ctrl+a (comme screen)
unbind C-b
set-option -g prefix C-a
bind-key C-a send-prefix

# Recharger la configuration
bind r source-file ~/.tmux.conf \; display-message "Config rechargée"

# Division plus intuitive (| pour horizontal, - pour vertical)
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"
unbind '"'
unbind %

# Navigation entre panneaux avec Alt+flèches (sans préfixe)
bind -n M-Left  select-pane -L
bind -n M-Right select-pane -R
bind -n M-Up    select-pane -U
bind -n M-Down  select-pane -D

# Numérotation des fenêtres à partir de 1
set -g base-index 1
set -g pane-base-index 1

# Couleurs 256
set -g default-terminal "screen-256color"

# Barre de statut personnalisée
set -g status-bg '#2d2d2d'
set -g status-fg '#ffffff'
set -g status-left '#[fg=#98be65,bold] [#S] '
set -g status-right '#[fg=#c678dd] %Y-%m-%d #[fg=#61afef] %H:%M '
set -g status-right-length 50

# Historique plus long
set-option -g history-limit 50000

# Mode souris (optionnel)
set -g mouse on

Exemple 28 (Workflow serveur distant avec tmux)

Voici un workflow typique pour administrer un serveur distant :

# 1. Connexion SSH au serveur
ssh alice@serveur.exemple.fr

# 2. Créer une session tmux nommée
tmux new -s travail

# 3. Ouvrir une fenêtre pour les logs
# Ctrl+b c pour une nouvelle fenêtre
# Dans la nouvelle fenêtre :
tail -f /var/log/app.log

# 4. Créer une fenêtre pour les commandes
# Ctrl+b c, puis Ctrl+b , pour la renommer "commandes"

# 5. Diviser pour surveiller les ressources
# Ctrl+b % pour diviser horizontalement
htop

# 6. Travailler normalement. Si la connexion tombe :
# Se reconnecter en SSH et :
tmux attach -t travail
# On retrouve exactement l'état d'avant !

```{prf:remark}
:label: remark-15-03
**Pourquoi tmux est indispensable sur un serveur distant ?** Sans tmux, si votre connexion SSH est interrompue (coupure réseau, fermeture accidentelle du terminal, timeout), tous vos processus en avant-plan reçoivent `SIGHUP` et se terminent. Avec tmux, la session tourne dans un processus `tmux server` sur le serveur, complètement indépendant de votre connexion SSH. Vous pouvez vous déconnecter (`Ctrl+b d`), éteindre votre ordinateur, vous reconnecter plus tard depuis une autre machine et reprendre exactement là où vous étiez, avec tous vos processus en cours.

screen : l’alternative classique#

screen est l’ancêtre de tmux, plus simple mais moins puissant. Il reste utile sur des systèmes anciens ou minimalistes où tmux n’est pas disponible.

# Démarrer screen
screen

# Démarrer avec un nom
screen -S nom_session

# Lister les sessions
screen -ls

# Se rattacher à une session
screen -r nom_session
screen -r    # S'il n'y en a qu'une

# Rattachement forcé (si la session est marquée "Attached")
screen -dr nom_session

# Raccourcis dans screen (préfixe : Ctrl+a)
# Ctrl+a d     : détacher
# Ctrl+a c     : nouvelle fenêtre
# Ctrl+a n     : fenêtre suivante
# Ctrl+a p     : fenêtre précédente
# Ctrl+a "     : liste des fenêtres
# Ctrl+a |     : diviser verticalement
# Ctrl+a S     : diviser horizontalement
# Ctrl+a Tab   : passer au panneau suivant
# Ctrl+a k     : tuer la fenêtre courante

direnv : variables d’environnement par répertoire#

direnv est un outil qui charge et décharge automatiquement des variables d’environnement selon le répertoire courant. C’est la solution la plus propre pour gérer des environnements de développement différents par projet.

Définition 53 (direnv)

direnv est un outil d’extension du shell qui surveille les déplacements dans le système de fichiers. Quand on entre dans un répertoire contenant un fichier .envrc, direnv charge automatiquement les variables définies dans ce fichier. Quand on quitte ce répertoire, ces variables sont automatiquement déchargées. direnv supporte Bash, Zsh, Fish et d’autres shells.

Installation et configuration#

# Installer direnv
sudo apt install direnv

# Activer direnv dans Bash (ajouter à ~/.bashrc)
eval "$(direnv hook bash)"

# Recharger la configuration
source ~/.bashrc

Utilisation#

# Dans un répertoire de projet, créer un .envrc
cd ~/projets/mon-api/
cat > .envrc << 'EOF'
export DATABASE_URL="postgresql://localhost:5432/mon_api_dev"
export REDIS_URL="redis://localhost:6379"
export API_KEY="dev_key_locale_123"
export LOG_LEVEL="debug"
export PORT=3000
EOF

# direnv bloque par sécurité les nouveaux .envrc :
# direnv: error /home/alice/projets/mon-api/.envrc is blocked.
# Autoriser explicitement
direnv allow .
# direnv: loading ~/projets/mon-api/.envrc
# direnv: export +API_KEY +DATABASE_URL +LOG_LEVEL +PORT +REDIS_URL

# Naviguer hors du répertoire décharge les variables
cd ~
# direnv: unloading

# Revenir dans le répertoire les recharge
cd ~/projets/mon-api/
# direnv: loading ~/projets/mon-api/.envrc

# Recharger manuellement après modification du .envrc
direnv reload

.envrc avancé#

# .envrc avec fonctionnalités avancées

# Charger un fichier .env (style Docker Compose)
dotenv .env.local
dotenv_if_exists .env.local   # Ne pas échouer si le fichier n'existe pas

# Ajouter au PATH
PATH_add ./node_modules/.bin
PATH_add ./bin
PATH_add ./venv/bin    # Virtualenv Python

# Activer un virtualenv Python
source_env_if_exists venv/bin/activate
layout python3    # Créer et activer un venv automatiquement

# Variables conditionnelles selon le hostname
if [[ "$(hostname)" == "prod-server" ]]; then
    export ENV="production"
else
    export ENV="development"
fi

# Utiliser un secret depuis un gestionnaire de mots de passe
export API_SECRET=$(pass show monprojet/api_secret)

# Hériter d'un .envrc parent
source_up_if_exists   # Charger le .envrc du répertoire parent si existant

Remarque 42

Il ne faut jamais committer le fichier .envrc s’il contient des valeurs secrètes (clés API, mots de passe). La bonne pratique est de versionner un fichier .envrc.example avec des valeurs fictives, et d’ajouter .envrc au .gitignore. Pour la même raison, direnv ne charge pas automatiquement un .envrc modifié sans que l’utilisateur ait explicitement accordé sa confiance via direnv allow : cela prévient les attaques par dépôts malveillants.

Personnalisation du prompt#

Le prompt Bash est contrôlé par la variable PS1. Il peut afficher des informations dynamiques grâce à des séquences spéciales.

# Variables disponibles dans PS1
# \u = nom d'utilisateur
# \h = nom d'hôte (court)
# \H = nom d'hôte complet (FQDN)
# \w = répertoire courant (complet)
# \W = répertoire courant (nom seulement)
# \$ = # si root, $ sinon
# \d = date
# \t = heure HH:MM:SS
# \j = nombre de jobs actifs
# \! = numéro de commande dans l'historique

# Codes couleur ANSI dans PS1
# \[\033[01;32m\] = vert gras
# \[\033[01;34m\] = bleu gras
# \[\033[01;31m\] = rouge gras
# \[\033[00m\]    = réinitialisation

# Prompt coloré classique : user@host:dossier$
PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '

# Prompt avec heure et jobs
PS1='[\t \j jobs] \u@\h:\w\$ '

# Prompt sur deux lignes
PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\n\$ '

# Inclure la branche git dans le prompt (nécessite git)
parse_git_branch() {
    git branch 2>/dev/null | grep '^*' | sed 's/* //'
}
PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[33m\]$(parse_git_branch)\[\033[00m\]\$ '

Visualisation : initialisation du shell Bash#

Hide code cell source

fig, axes = plt.subplots(1, 2, figsize=(16, 10))
palette = sns.color_palette("muted", 10)

def draw_box(ax, x, y, w, h, titre, detail, color, fontsize_t=9.5):
    rect = patches.FancyBboxPatch(
        (x, y), w, h,
        boxstyle="round,pad=0.1", linewidth=2,
        edgecolor=color, facecolor=color, alpha=0.2
    )
    ax.add_patch(rect)
    border = patches.FancyBboxPatch(
        (x, y), w, h,
        boxstyle="round,pad=0.1", linewidth=2,
        edgecolor=color, facecolor='none'
    )
    ax.add_patch(border)
    ax.text(x + w/2, y + h * 0.65, titre, ha='center', va='center',
            fontsize=fontsize_t, fontweight='bold',
            fontfamily='monospace', color=color)
    if detail:
        ax.text(x + w/2, y + h * 0.3, detail, ha='center', va='center',
                fontsize=7.5, color='#555', style='italic')

def arrow(ax, x1, y1, x2, y2, color='#555', label=''):
    ax.annotate('', xy=(x2, y2), xytext=(x1, y1),
                arrowprops=dict(arrowstyle='->', color=color, lw=2))
    if label:
        mx, my = (x1+x2)/2, (y1+y2)/2
        ax.text(mx + 0.1, my, label, fontsize=8, color=color, va='center')

# ---- Graphique 1 : shell de LOGIN interactif ----
ax = axes[0]
ax.set_xlim(0, 10)
ax.set_ylim(0, 11)
ax.axis('off')
ax.set_title('Shell interactif de LOGIN\n(ssh, tty, su -)', fontsize=12,
             fontweight='bold', color=palette[0])

etapes_login = [
    (2.5, 9.5, 5.0, 0.8, "/etc/profile",        "Config globale système",        palette[0]),
    (2.5, 8.2, 5.0, 0.8, "/etc/profile.d/*.sh",  "Scripts système additionnels",  palette[1]),
    (2.5, 6.5, 5.0, 0.8, "~/.bash_profile",      "Config perso login (priorité 1)", palette[2]),
    (2.5, 5.2, 5.0, 0.8, "~/.bash_login",        "Si pas de .bash_profile",       palette[3]),
    (2.5, 3.9, 5.0, 0.8, "~/.profile",           "Si pas des deux précédents",    palette[4]),
    (2.5, 2.3, 5.0, 0.8, "~/.bashrc",            "Sourcé par .bash_profile",      palette[5]),
    (2.5, 1.0, 5.0, 0.8, "~/.bash_logout",       "À la fermeture de session",     palette[6]),
]

for i, (x, y, w, h, titre, detail, col) in enumerate(etapes_login[:-1]):
    draw_box(ax, x, y, w, h, titre, detail, col)

# bash_logout séparé (en bas, avec flèche différente)
x, y, w, h, titre, detail, col = etapes_login[-1]
draw_box(ax, x, y, w, h, titre, detail, col)

# Flèches de séquence
for i in range(len(etapes_login) - 3):
    x1 = etapes_login[i][0] + etapes_login[i][2] / 2
    y1 = etapes_login[i][1]
    y2 = etapes_login[i+1][1] + etapes_login[i+1][3]
    arrow(ax, x1, y1, x1, y2, palette[i+1])

# Flèche spéciale vers .bashrc depuis .bash_profile
ax.annotate('', xy=(5.0, 3.05), xytext=(5.0, 5.2),
            arrowprops=dict(arrowstyle='->', color=palette[5], lw=1.8,
                            linestyle='dashed'))
ax.text(5.3, 4.1, 'source ~/.bashrc', fontsize=7.5, color=palette[5],
        style='italic')

# Flèche de sortie vers .bash_logout
ax.annotate('', xy=(5.0, 1.8), xytext=(5.0, 0.5),
            arrowprops=dict(arrowstyle='<-', color=palette[6], lw=1.8,
                            linestyle='dashed'))
ax.text(5.1, 1.3, 'à la sortie', fontsize=7.5, color=palette[6], style='italic')

# ---- Graphique 2 : shell NON-LOGIN interactif et non-interactif ----
ax = axes[1]
ax.set_xlim(0, 10)
ax.set_ylim(0, 11)
ax.axis('off')
ax.set_title('Shell non-login interactif vs non-interactif',
             fontsize=12, fontweight='bold', color=palette[2])

# Bloc titre gauche
ax.text(2.5, 10.3, 'Non-login interactif', ha='center', fontsize=10.5,
        fontweight='bold', color=palette[2],
        bbox=dict(boxstyle='round,pad=0.3', facecolor=palette[2], alpha=0.2))
ax.text(2.5, 9.7, '(terminal GNOME, bash dans bash)', ha='center',
        fontsize=8, color='#555', style='italic')

draw_box(ax, 0.5, 8.5, 4.0, 0.8, "/etc/bash.bashrc", "Config Bash système", palette[1])
draw_box(ax, 0.5, 7.2, 4.0, 0.8, "~/.bashrc", "Config perso (TOUT va ici)", palette[2])
arrow(ax, 2.5, 8.5, 2.5, 8.0, palette[2])

# Bloc titre droite
ax.text(7.5, 10.3, 'Non-interactif', ha='center', fontsize=10.5,
        fontweight='bold', color=palette[5],
        bbox=dict(boxstyle='round,pad=0.3', facecolor=palette[5], alpha=0.2))
ax.text(7.5, 9.7, '(scripts bash, cron, CI/CD)', ha='center',
        fontsize=8, color='#555', style='italic')

draw_box(ax, 5.5, 8.5, 4.0, 0.8, "$BASH_ENV", "Si défini, sourcé", palette[5])
draw_box(ax, 5.5, 7.2, 4.0, 0.8, "(aucun autre fichier)", "Environnement hérité", palette[6])
arrow(ax, 7.5, 8.5, 7.5, 8.0, palette[5])

# Séparateur
ax.plot([5.0, 5.0], [6.5, 11.0], color='#ccc', linewidth=1.5, linestyle='--')

# Récap contenu de .bashrc
draw_box(ax, 0.5, 4.5, 9.0, 2.2, "~/.bashrc — contenu typique", "", palette[2], fontsize_t=10)

contenus = [
    "Alias (ll, gs, grep --color…)",
    "Fonctions personnalisées",
    "Options shell (shopt -s histappend…)",
    "Historique (HISTSIZE, HISTCONTROL)",
    "Prompt (PS1)",
    "PATH (export PATH=…)",
    "Chargement outils (nvm, pyenv, cargo…)",
]
for i, item in enumerate(contenus):
    col_i = i % 2
    row_i = i // 2
    x_item = 0.7 + col_i * 4.5
    y_item = 6.35 - row_i * 0.45
    ax.text(x_item, y_item, f"• {item}", va='center', fontsize=8.5,
            color='#333')

# Récap .bash_profile
draw_box(ax, 0.5, 2.2, 9.0, 1.8, "~/.bash_profile — contenu typique", "", palette[0], fontsize_t=10)
items_profile = [
    "source ~/.bashrc",
    "export EDITOR, VISUAL, PAGER",
    "export PATH (répertoires login)",
    "Initialisation outils lourds (pyenv, rbenv…)",
]
for i, item in enumerate(items_profile):
    col_i = i % 2
    row_i = i // 2
    x_item = 0.7 + col_i * 4.5
    y_item = 3.7 - row_i * 0.45
    ax.text(x_item, y_item, f"• {item}", va='center', fontsize=8.5,
            color='#333')

# Note sur .profile
draw_box(ax, 0.5, 0.5, 9.0, 1.3,
         "~/.profile — utilisé si pas de .bash_profile",
         "Syntaxe POSIX sh (pas spécifique à Bash)", palette[3])

plt.tight_layout()
plt.show()
_images/8f3c512e21e2ebcd1b1b9cc19ab4d1b540f27a17620ea3cdd443bdcb02e636c0.png

Résumé#

Ce chapitre a couvert la configuration complète de l’environnement shell Bash :

  • Bash distingue les shells de login (qui lisent /etc/profile puis ~/.bash_profile) des shells non-login interactifs (qui lisent ~/.bashrc). La convention est de faire sourcer ~/.bashrc depuis ~/.bash_profile pour unifier la configuration.

  • Les alias permettent des raccourcis de commandes simples et sont rendus permanents dans ~/.bashrc ou ~/.bash_aliases. Les fonctions permettent des raccourcis plus complexes acceptant des arguments.

  • Le PATH est la liste ordonnée des répertoires dans lesquels Bash cherche les commandes. ~/.local/bin est le répertoire conventionnel pour les commandes personnelles.

  • Les variables d’environnement (HOME, USER, SHELL, PATH, LANG, EDITOR, TERM, etc.) configurent le comportement du shell et de tous les processus enfants.

  • tmux est l’outil indispensable pour les sessions persistantes sur serveur distant : il permet de se détacher (Ctrl+b d) et de se rattacher sans perdre le travail en cours. La hiérarchie sessions/fenêtres/panneaux offre une organisation flexible de l’espace de travail.

  • direnv permet de charger et décharger automatiquement des variables d’environnement selon le répertoire courant, via des fichiers .envrc par projet, sans polluer l’environnement global.

Ces outils de configuration forment la base d’un environnement de travail Linux professionnel, reproductible et efficace. Le chapitre suivant abordera les expressions régulières et les outils de traitement de texte : grep, sed, awk et leurs usages avancés dans les scripts de traitement de données.