Aller au contenu
Administration Linux medium

Fish shell : installer et configurer config.fish

20 min de lecture

Logo Fish

Fish (Friendly Interactive Shell) est un shell interactif confortable dès l'installation, sans configuration : autosuggestions tirées de l'historique, coloration syntaxique en direct et complétion intelligente sont actives par défaut. Là où Bash et Zsh demandent des heures de réglages pour atteindre ce confort, Fish le fournit d'emblée, au prix d'une syntaxe volontairement non POSIX. Cette page montre comment l'installer, le configurer via config.fish, écrire des fonctions persistantes et éviter le piège du PATH qui duplique silencieusement vos chemins. Les commandes sont validées sur Fish 4.0.2, la version livrée par Debian 13.

  • Installer Fish sur les principales distributions et l'essayer sans changer de shell par défaut
  • Configurer config.fish et ajouter un chemin au PATH sans le corrompre
  • Écrire des fonctions persistantes, les déboguer et les supprimer proprement
  • Utiliser les vrais raccourcis clavier de Fish, vérifiés avec la commande bind
  • Savoir quand ne pas utiliser Fish : les scripts d'administration restent en Bash

Fish est packagé dans les dépôts officiels de toutes les distributions courantes. Aucun dépôt tiers, aucun script d'installation distant : le gestionnaire de paquets de votre distribution suffit, ce qui vous garantit des mises à jour de sécurité suivies par la distribution.

Fenêtre de terminal
sudo apt update
sudo apt install fish

Si le gestionnaire de paquets apt ne vous est pas familier, le guide apt détaille son fonctionnement. Sous Windows, Fish s'installe dans WSL 2 avec la commande Debian/Ubuntu ci-dessus. Sous macOS, l'installation passe par Homebrew.

Vérification : la commande suivante doit afficher la version installée.

Fenêtre de terminal
fish --version
fish, version 4.0.2

Essayer Fish avant d'en faire votre shell par défaut

Section intitulée « Essayer Fish avant d'en faire votre shell par défaut »

Changer de shell par défaut avant d'avoir testé est un risque inutile : si votre config.fish contient une erreur, vous vous retrouvez avec un shell de connexion cassé. La bonne méthode consiste à lancer Fish comme une commande ordinaire, à l'utiliser quelques jours, et à ne basculer qu'ensuite. Vous quittez Fish avec exit et retrouvez votre shell précédent.

Fenêtre de terminal
fish

Une fois convaincu, déclarez Fish comme shell de connexion. La commande chsh refuse tout interpréteur absent du fichier /etc/shells (la liste blanche des shells de connexion autorisés), d'où la vérification préalable.

  1. Vérifiez que Fish est déclaré comme shell de connexion valide :

    Fenêtre de terminal
    grep fish /etc/shells

    Si la commande ne renvoie rien, ajoutez le chemin de l'exécutable :

    Fenêtre de terminal
    command -v fish | sudo tee -a /etc/shells
  2. Changez de shell pour votre compte utilisateur. La forme $(...) est utilisée ici parce qu'elle fonctionne aussi bien depuis Bash que depuis Fish, alors que la forme (...) propre à Fish provoquerait une erreur de syntaxe si vous êtes encore dans Bash :

    Fenêtre de terminal
    chsh -s $(command -v fish)
  3. Vérifiez la prise en compte en relisant la base des comptes, puis reconnectez-vous :

    Fenêtre de terminal
    getent passwd $USER

    La ligne doit se terminer par /usr/bin/fish.

Toute la configuration de Fish vit dans ~/.config/fish/. Le fichier ~/.config/fish/config.fish est lu à chaque démarrage d'un shell Fish : c'est là que vous placez vos réglages d'environnement. Le sous-répertoire ~/.config/fish/functions/ contient vos fonctions, chargées automatiquement et à la demande (Fish les lit quand vous appelez la fonction, pas au démarrage).

Pour l'apparence, Fish fournit fish_config, qui ouvre une interface web locale de personnalisation du thème d'invite.

Fenêtre de terminal
fish_config

Fish Config

Sur un serveur en SSH, aucun navigateur n'est disponible : la même commande fonctionne alors en ligne de commande, ce qui est souvent plus rapide. Vous listez les invites disponibles, puis vous en choisissez une et vous l'enregistrez.

Fenêtre de terminal
fish_config prompt list
acidhub
arrow
astronaut
default
disco
informative
Fenêtre de terminal
fish_config prompt choose informative
fish_config prompt save

Vérification : ouvrez un nouveau terminal, l'invite doit avoir changé. Le choix est écrit dans ~/.config/fish/functions/fish_prompt.fish.

C'est l'erreur la plus fréquente quand on arrive de Bash, et elle mérite qu'on s'y arrête. En Bash, on écrit export PATH=$PATH:/nouveau/chemin. Transposée telle quelle en Fish, cette ligne ne fait pas ce que vous croyez.

En Fish, une variable est une liste, jamais une chaîne. Coller une chaîne à côté d'une liste ne concatène pas : cela produit un produit cartésien, c'est-à-dire que le suffixe est collé à chaque élément de la liste.

Fenêtre de terminal
set liste a b c
echo $liste:/suffixe
a:/suffixe b:/suffixe c:/suffixe

Appliqué au PATH, l'effet est sournois. Comme PATH est une variable de chemin que Fish redécoupe sur les deux-points, vos commandes continuent de fonctionner, ce qui vous laisse croire que tout va bien. En réalité, vous ajoutez une copie du nouveau chemin par entrée existante, et le PATH double de taille à chaque rechargement de config.fish.

Fenêtre de terminal
set -gx PATH /usr/bin /bin
set -gx PATH $PATH:/opt/bin # à ne pas reproduire
count $PATH
4

Rejouez cette même ligne deux fois de plus, comme le ferait un source ~/.config/fish/config.fish, et le compteur passe à 8 puis 16 entrées. Le PATH enfle jusqu'à ralentir la résolution de chaque commande.

La bonne réponse est fish_add_path, la commande prévue pour cela. Elle est idempotente (l'appeler dix fois n'ajoute qu'une entrée), elle persiste toute seule via la variable universelle fish_user_paths, et elle place le chemin en tête du PATH.

Fenêtre de terminal
fish_add_path /opt/outils/bin

Vérification : contains renvoie le code de retour 0 quand l'élément est bien présent dans la liste.

Fenêtre de terminal
contains /opt/outils/bin $PATH; echo $status
0

Parce que fish_add_path écrit dans une variable universelle, vous n'avez rien à ajouter dans config.fish : le chemin est déjà persistant pour toutes vos sessions. Pour retirer un chemin, agissez sur cette même variable fish_user_paths.

Fenêtre de terminal
set -U fish_user_paths (string match -v /opt/outils/bin $fish_user_paths)

Si vous tenez à passer par config.fish, la syntaxe correcte sépare les éléments par des espaces, jamais par des deux-points.

Fenêtre de terminal
set -gx PATH $PATH /opt/outils/bin

Les fonctions regroupent plusieurs commandes sous un même nom. En Fish, elles remplacent avantageusement les alias, car elles acceptent des arguments et de la logique. Les arguments arrivent dans la liste $argv, indexée à partir de 1 et non de 0.

Fenêtre de terminal
function salut
echo "Salut, $argv[1] ! Comment vas-tu ?"
end
salut Alice
Salut, Alice ! Comment vas-tu ?

Une fonction définie au clavier disparaît avec la session. Pour la conserver, funcsave l'écrit sur disque dans ~/.config/fish/functions/, où Fish la rechargera automatiquement dans tous vos futurs terminaux. C'est le seul mécanisme de persistance des fonctions : il n'existe pas de « fonction universelle » en Fish, contrairement aux variables que set -U sait rendre universelles.

Fenêtre de terminal
funcsave salut
funcsave: wrote /root/.config/fish/functions/salut.fish

Les alias suivent exactement le même chemin : alias fabrique en réalité une fonction, que funcsave sait donc enregistrer.

Fenêtre de terminal
alias ll="ls -la"
funcsave ll

Pour relire une fonction, appelez functions avec son nom : la commande affiche son code source, description comprise. C'est aussi la façon de vérifier ce que fait une fonction héritée d'un plugin.

Fenêtre de terminal
function addition --description "Ajoute deux nombres"
math $argv[1] + $argv[2]
end
functions addition
# Defined interactively
function addition --description 'Ajoute deux nombres'
math $argv[1] + $argv[2];
end

Quand une fonction ne fait pas ce que vous attendez, vous voulez voir chaque commande réellement exécutée. Fish fournit pour cela la variable fish_trace : dès qu'elle vaut 1, le shell affiche les commandes au fur et à mesure, préfixées par > pour l'appel et --> pour les commandes internes.

Fenêtre de terminal
set fish_trace 1
addition 2 3
set -e fish_trace
> addition 2 3
--> math 2 + 3
5

Pensez bien à désactiver la trace avec set -e fish_trace une fois le diagnostic terminé, sinon votre terminal reste bavard. Pour les problèmes qui touchent le shell lui-même plutôt qu'une de vos fonctions, fish --debug=<catégorie> active les journaux internes, par exemple fish --debug=complete pour instrumenter la complétion.

Fish peut déclencher une fonction quand un événement survient, grâce à l'option --on-event. Les événements réellement émis par le shell sont peu nombreux et portent des noms précis : fish_prompt (avant chaque affichage de l'invite), fish_preexec et fish_postexec (juste avant et juste après l'exécution d'une commande), fish_exit (à la fermeture du shell) ou encore fish_command_not_found.

Fenêtre de terminal
function au_revoir --on-event fish_exit
echo "Session terminée."
end

Le piège est que Fish accepte n'importe quel nom d'événement sans broncher, y compris un nom qui n'existe pas. La fonction est alors bien enregistrée, visible dans functions --handlers, mais elle ne se déclenchera jamais, et aucun message d'erreur ne vous préviendra. Un --on-event session-start, par exemple, ne produit strictement rien, car Fish n'émet aucun événement de ce nom.

Pour exécuter du code au démarrage d'un shell, n'inventez donc pas d'événement : placez simplement vos commandes dans config.fish, qui est lu à chaque ouverture de session.

Fish n'a pas de mot-clé export : tout passe par la commande set, dont une option détermine la portée. C'est plus explicite que Bash, à condition de connaître les trois portées et de choisir la bonne, faute de quoi vos réglages disparaissent au prochain terminal.

PortéeOptionDurée de vie
Locale (défaut dans un bloc)set -lLe bloc ou la fonction en cours
Globaleset -gLa session en cours
Universelleset -UToutes les sessions, persistée sur disque
Exportée aux sous-processus-x (combinable)Selon la portée choisie

La portée universelle est la spécificité de Fish : set -Ux EDITOR vim est immédiatement visible dans tous vos terminaux ouverts, sans toucher au moindre fichier de configuration. C'est le bon réflexe pour une préférence personnelle stable.

Fenêtre de terminal
set -Ux EDITOR vim

Vérification : set -S affiche la portée réelle d'une variable, ce qui lève tout doute.

Fenêtre de terminal
set -S EDITOR

Pour supprimer une variable, quelle que soit sa portée, utilisez set -e suivi de son nom.

Fenêtre de terminal
set -e EDITOR

Fish est confortable surtout parce que ses raccourcis sont déjà en place. Le tableau ci-dessous ne liste que des liaisons vérifiées avec la commande bind sur Fish 4.0.2 : c'est votre source de vérité, et vous pouvez la relancer à tout moment pour inspecter votre propre configuration.

RaccourciAction réelle
ou Ctrl + FAccepte l'autosuggestion affichée en gris
TabOuvre la complétion
Ctrl + RRecherche dans l'historique (pageur interactif)
Ctrl + A / Ctrl + EDébut / fin de ligne
Ctrl + U / Ctrl + KEfface avant / après le curseur
Ctrl + WEfface le composant de chemin précédent
Ctrl + DEfface le caractère, ou quitte si la ligne est vide
Alt + ← / Alt + →Ligne vide : répertoire précédent / suivant. Sinon : mot par mot
Alt + ↑ / Alt + ↓Recherche dans l'historique le token sous le curseur
Alt + .Insère le dernier argument de la commande précédente
Alt + DLigne vide : affiche l'historique des répertoires. Sinon : efface le mot suivant
Ctrl + SActive la recherche dans le pageur de complétion

Deux liaisons méritent une lecture attentive, car elles changent de comportement selon le contexte. Alt + ← et Alt + → naviguent dans l'historique des répertoires uniquement quand la ligne de commande est vide ; dès que vous avez tapé quelque chose, elles déplacent le curseur mot par mot. Les commandes équivalentes sont prevd et nextd, et dirh affiche la pile complète.

Fenêtre de terminal
cd /tmp/projet
cd /var/log
prevd
pwd
/tmp/projet

Vous pouvez évidemment définir vos propres liaisons avec bind, en utilisant les noms de touches de Fish 4 (ctrl-t, alt-p). L'ancienne notation à échappement (\ct) reste acceptée, mais les noms lisibles sont plus faciles à relire dans un config.fish.

Fenêtre de terminal
bind ctrl-t 'date'

Vérification : bind | grep ctrl-t doit afficher votre liaison. Ajoutez la ligne à config.fish pour la conserver.

Fisher est le gestionnaire de plugins le plus utilisé de l'écosystème Fish. Son installation officielle propose un curl | source, c'est-à-dire l'exécution directe de code téléchargé : ne faites jamais cela. Vous exécuteriez dans votre shell un fichier que vous n'avez pas lu, servi par un serveur distant, sans aucune vérification. Téléchargez, relisez, puis chargez.

  1. Téléchargez la fonction Fisher dans un fichier local :

    Fenêtre de terminal
    curl -sL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish -o fisher.fish
  2. Relisez le fichier avant toute exécution. Vous devez y voir du code Fish lisible, commençant par function fisher :

    Fenêtre de terminal
    less fisher.fish
  3. Chargez-le dans la session, puis laissez Fisher s'installer lui-même :

    Fenêtre de terminal
    source fisher.fish && fisher install jorgebucaran/fisher

Une fois Fisher en place, l'installation d'un plugin tient en une commande. Les plugins vivent dans votre configuration utilisateur, sans privilège root.

Fenêtre de terminal
fisher install jorgebucaran/nvm.fish
fisher list

Pour retirer un plugin, fisher remove suivi de son nom suffit. Le catalogue communautaire est publié sur awsm.fish.

Fish n'est pas compatible POSIX, et c'est assumé. Un script Bash collé dans un terminal Fish échouera dès la première boucle ou le premier [[ ... ]]. La règle est simple : Fish pour l'interactif : Bash pour les scripts. Vos scripts existants s'exécutent normalement en les appelant explicitement, puisqu'ils déclarent leur propre interpréteur.

Fenêtre de terminal
bash ./deploiement.sh

Une idée reçue mérite d'être corrigée : on lit souvent que Fish ignore la syntaxe $(...). C'était vrai autrefois, mais Fish la prend en charge depuis la version 3.4 (2022). Les deux formes de substitution de commande fonctionnent donc, la forme historique entre parenthèses seules et la forme compatible Bash.

Fenêtre de terminal
echo "Nous sommes le (date +%d/%m)"
echo "Nous sommes le $(date +%d/%m)"

Restent les vraies divergences : set remplace export, les listes remplacent les tableaux, et les blocs se ferment par end plutôt que par fi ou done.

Voici les blocages les plus courants lors d'un passage à Fish, avec la cause et la correction directement applicable.

SymptômeCause probableSolution
chsh: PAM: Authentication failureMot de passe erroné, ou Fish absent de /etc/shellsVérifier avec grep fish /etc/shells et ajouter le chemin : command -v fish | sudo tee -a /etc/shells
Le PATH contient des doublons qui se multiplientset -gx PATH $PATH:/chemin (produit cartésien)Repartir de fish_add_path /chemin, ou séparer par des espaces
Un chemin ajouté n'apparaît pas dans $PATHLe répertoire n'existe pas : fish_add_path l'ignore en silencetest -d /chemin; echo $status doit renvoyer 0
Une fonction supprimée réapparaît au prochain terminalfunctions -e n'efface que la session ; le fichier resterm ~/.config/fish/functions/<nom>.fish
Une fonction --on-event ne se déclenche jamaisNom d'événement inexistant, accepté sans erreurUtiliser un événement réel (fish_prompt, fish_exit) ou passer par config.fish
Les scripts Bash échouent dans FishFish n'est pas compatible POSIXLes lancer explicitement : bash script.sh
fish_config n'ouvre pas de navigateurSession SSH sans interface graphiqueUtiliser fish_config prompt list puis choose et save

Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

6 questions
6 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

  • Fish est confortable sans configuration : autosuggestions, coloration et complétion sont actives dès l'installation.
  • Essayez Fish en le lançant comme une commande avant de faire un chsh qui engage votre shell de connexion.
  • Le PATH ne s'étend pas avec : : set -gx PATH $PATH:/chemin produit un produit cartésien qui double le PATH à chaque rechargement. Utilisez fish_add_path.
  • Les fonctions universelles n'existent pas : seules les variables peuvent être universelles (set -U). Une fonction se persiste avec funcsave, dans ~/.config/fish/functions/.
  • functions -e n'efface que la session : supprimez aussi le fichier pour une suppression durable.
  • Déboguez avec set fish_trace 1, et non avec une option --trace qui n'existe pas.
  • $(...) fonctionne depuis Fish 3.4 : la substitution de commande à la Bash est prise en charge.
  • Fish pour l'interactif, Bash pour les scripts : la portabilité de vos automatisations n'est pas négociable.

Fish naît en 2005, à une époque où Bash et Zsh dominent sans partage et exigent de longues configurations pour devenir ergonomiques. Son pari : s'affranchir des conventions Unix quand elles nuisent à l'utilisateur, plutôt que de les perpétuer par tradition. Ce choix lui vaut son incompatibilité POSIX, mais aussi son confort immédiat.

Les jalons marquants de son évolution éclairent ce que vous utilisez aujourd'hui :

  • 2009 : arrivée de l'interface web de configuration (fish_config).
  • 2013 : Fish 2.0 améliore les scripts et les raccourcis clavier.
  • 2016 : le gestionnaire de plugins Fisher s'impose dans l'écosystème.
  • 2022 : Fish 3.4 ajoute la substitution de commande $(...), qui rapproche la syntaxe de Bash.
  • 2025 : Fish 4.0 est entièrement réécrit en Rust, après vingt ans de C++. Le comportement ne change pas pour l'utilisateur, mais la base de code gagne en sûreté mémoire et en maintenabilité. C'est la branche livrée par Debian 13 en version 4.0.2.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn