# Customize your shell with ZSH A series of 7 article(s), in reading order. # Personnalisez votre prompt Linux avec Powerlevel10k > Testez et personnalisez le thème Zsh Powerlevel10k pour votre prompt Linux ou WSL dans un sandbox Docker jetable avant de vous lancer dans une installation complète. --- ![Personnalisez votre prompt Linux avec Powerlevel10k](/img/v2/customization_prompt.webp) **TL;DR** Cet article montre comment essayer le thème de prompt Zsh Powerlevel10k sans aucun risque, grâce à un container Docker Alpine jetable (une commande unique tirée du README officiel) : tout — git, zsh, nano, le thème et son assistant de configuration — tourne entièrement en RAM et disparaît au `exit`. De quoi décider si vous l'installez pour de bon avant de toucher à votre vraie machine. Quand vous travaillez sous Linux (donc aussi avec WSL), il existe de nombreuses façons de personnaliser votre prompt. Une des solutions les plus simples est d'utiliser [Powerlevel10k](https://github.com/romkatv/powerlevel10k) et son assistant. Dans cet article, nous allons utiliser un container Docker uniquement pour : *tester et jeter*. Vous verrez exactement à quoi ressemble le prompt, vous jouerez avec l'assistant, et votre machine restera intacte. L'astuce vient de [https://github.com/romkatv/powerlevel10k/blob/master/README.md](https://github.com/romkatv/powerlevel10k/blob/master/README.md#try-it-in-docker) ## À quoi ressemble Powerlevel10k {#what-powerlevel10k-looks-like} Voici mon propre prompt, une fois l'assistant passé : ![Un prompt Powerlevel10k](./images/p10k-prompt.webp) Lisez-le de gauche à droite : le dossier courant, puis la branch git (`main`) avec `?1` qui m'indique qu'un fichier est modifié et pas encore commité, puis — à droite — une coche verte pour la commande précédente, l'identité `root@AVONTURE-RACOUR` avec laquelle je suis connecté, et l'heure de fin de la commande. Tout ça en permanence, sans taper `git status` ni `whoami`. Voilà l'argument ; le reste de cet article explique comment l'essayer sans toucher à votre machine. ## L'essayer sans rien installer {#try-it-without-installing-anything} En lançant la commande ci-dessous, vous allez télécharger une toute petite image Linux Alpine puis démarrer quelques initialisations comme l'installation de `git`, `nano`, `zsh`, ... Le repository Powerlevel10k sera téléchargé depuis Github et son assistant sera lancé. ```bash $ docker run -e TERM -e COLORTERM -e LC_ALL=C.UTF-8 -it --rm alpine sh -uec ' apk add git zsh nano vim git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ~/powerlevel10k echo "source ~/powerlevel10k/powerlevel10k.zsh-theme" >>~/.zshrc cd ~/powerlevel10k exec zsh' ``` Répondez aux questions de l'assistant, jouez avec le résultat, et décidez si vous l'adoptez ou non. > **Tout se passe en RAM ; rien sur votre disque:** > > Lancer la commande `docker run` ci-dessus va télécharger une image Docker Alpine Linux sur votre disque (moins de 7 Mo) puis installer des binaires dans le container en cours d'exécution. Donc, en quittant le container avec la commande `exit`, rien ne restera sur votre disque. Idéal pour tester. ## Pourquoi je l'ai gardé {#why-i-kept-it} * Quand une instruction est terminée, le nouveau prompt affiche le temps qu'elle a pris, pratique quand vous cherchez à optimiser une commande, * À droite, vous voyez immédiatement si l'instruction a échoué, avec un affichage rouge et le code d'erreur (`exitcode`), * Il s'intègre bien avec [Windows Terminal](/blog/windows-terminal) si vous êtes sous WSL, et avec la configuration modulaire décrite dans [Beyond the Monolith - Organizing Your ZSH Workflow Like a Pro](/blog/modular-zsh-workflow), * Et, bien sûr, l'aspect visuel, qui est plutôt sympa. Et aussi, comme je travaille dans des containers Docker au quotidien, utiliser Powerlevel10k en local me donne une indication visuelle forte pour savoir à tout moment si je suis en local ou dans un container. ## Conclusion {#conclusion} L'avantage de ce sandbox, c'est que la décision ne vous coûte rien : vous tapez une commande, vous regardez le prompt pendant deux minutes, et vous tapez `exit`. Tout ce que vous avez répondu à l'assistant disparaît avec le container. Si ce que vous avez vu vous plaît, faites-le pour de vrai : [How to install Oh-My-ZSH](/blog/zsh-install) couvre l'installation sur disque (Oh-My-Zsh d'abord, puis Powerlevel10k), et le [guide d'installation](https://github.com/romkatv/powerlevel10k#installation) officiel donne les détails pour toutes les autres configurations. --- # Comment installer Oh-My-ZSH > Installez Zsh et Oh-My-ZSH facilement ! Apprenez à booster votre terminal Linux avec le thème Powerlevel10k, des fonctions comme take et les fonctionnalités indispensables au quotidien. --- ![Comment installer Oh-My-ZSH](/img/v2/zsh.webp) **TL;DR** Cet article détaille l'installation de Zsh et Oh My Zsh sur Linux, puis l'amélioration du prompt avec le thème Powerlevel10k et la suppression de l'affichage par défaut du nom d'utilisateur et de la machine. Il met aussi en avant quelques fonctionnalités du quotidien à connaître : la fonction `take` pour créer un dossier et y entrer en une seule étape, les enchaînements `cd ...` pour remonter plusieurs dossiers parents, l'autocomplétion avec `TAB`, et les alias Git comme `gst` et `gwip`. ZSH est une alternative puissante au Bash de Linux qui offre de nombreuses fonctionnalités : autocomplétion (j'adore ça), plugins et même thèmes. *Une fois installé, deux articles vont plus loin : [Beyond the Monolith - Organizing Your ZSH Workflow Like a Pro](/blog/modular-zsh-workflow) pour éviter que votre `.zshrc` devienne un monstre, et [ZSH Functions - Customizing Your Shell for Docker Management](/blog/zsh-docker-functions).* L'idée ici est de booster votre console Linux : améliorer la ligne de commande (par ex. de nouveaux alias disponibles immédiatement) et rendre l'apparence encore plus agréable. J'utilise [Oh My ZSH](https://ohmyz.sh/) depuis des années ; voyons comment l'installer, puis découvrons quelques fonctionnalités. ## Où on va {#where-were-going} Voici mon prompt une fois Zsh, Oh-My-Zsh et le thème Powerlevel10k en place : ![Powerlevel10k - Prompt sans nom d'utilisateur](./images/powerlevel10k_prompt_no_user.webp) D'un coup d'œil, je sais que je suis dans mon dossier `blog`, qu'il s'agit d'un repository Git, que je suis sur la branch `main` et que `?1` signifie qu'un fichier *untracked* ou *modifié* attend sur mon ordinateur, pas encore poussé sur GitHub. Aucune commande tapée, aucun `git status` lancé : l'information est simplement là, rafraîchie à chaque prompt. ## Pourquoi ça vaut les dix minutes {#why-its-worth-the-ten-minutes} * **Oh-My-Zsh est un framework, pas un thème.** Il embarque des centaines d'alias et de fonctions dès l'installation : vous y gagnez avant même d'avoir configuré quoi que ce soit. * **Le prompt devient un tableau de bord.** Dossier, repository, branch et nombre de modifications en attente sont affichés en permanence, c'est-à-dire exactement l'état que vous vérifiez sans cesse à la main. * **L'autocomplétion est d'un autre niveau.** `TAB` liste les sous-dossiers, les flags des commandes et les branches Git, au lieu de simplement compléter un nom de fichier. ## Le voir à l'œuvre avec Docker {#seeing-it-in-action-with-docker} Avant de toucher à votre `~/.zshrc`, lancez un container jetable et jouez réellement avec Oh-My-Zsh. > **Pourquoi Docker d'abord ?:** > > Un container vous permet de ressentir ce que change Oh-My-Zsh — le prompt, les alias, les astuces du quotidien ci-dessous — sans changer votre shell de connexion ni modifier le moindre fichier sur votre machine. Ça ne vous plaît pas ? Supprimez le container, il ne reste rien. Voici le Dockerfile que j'ai préparé. Il installe Oh-My-Zsh sans intervention et prépare aussi un petit terrain de jeu : une arborescence profonde pour essayer `cd ...`, et un repository git avec un commit et un fichier untracked pour que `gst` ait quelque chose à montrer : **Dockerfile** ```docker FROM ubuntu:24.04 ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update && \ apt-get install -y --no-install-recommends \ zsh \ git \ curl \ ca-certificates && \ rm -rf /var/lib/apt/lists/* # Install Oh-My-Zsh unattended: no interactive prompt, no shell switch, keep # the default .zshrc so it can be inspected/edited straight away. RUN sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" \ "" --unattended --keep-zshrc # Pre-build a demo playground so the daily-use features have something real # to act on: a deep folder tree (for `cd ...`) inside a git repo with a # commit and an untracked file (for `gst`, the `git status` alias). RUN git config --global user.email "demo@example.com" && \ git config --global user.name "Demo" && \ mkdir -p /root/demo/a/b/c/d/e/f && \ cd /root/demo && \ git init -q -b main && \ echo "# Demo repo" > README.md && \ git add README.md && \ git commit -q -m "Initial commit" && \ echo "draft notes, not committed" > notes.txt WORKDIR /root/demo/a/b/c/d/e/f # Signal clearly that this shell runs inside a container (overrides the # Oh-My-Zsh theme prompt on purpose) RUN echo "PROMPT='%B%F{yellow}🐳 %F{cyan}%n%f%b %~ %# '" >> /root/.zshrc CMD ["/bin/zsh", "-i", "-l"] ``` Construisez-le et lancez-le : ```bash $ docker build -t zsh-demo . [+] Building 22.1s (8/8) FINISHED ✔ exporting to image $ docker run --rm -it zsh-demo 🐳 root ~/demo/a/b/c/d/e/f # ``` Vous arrivez directement dans `/root/demo/a/b/c/d/e/f` — six dossiers de profondeur, repo git déjà initialisé. Essayez tout de suite les fonctionnalités de la section suivante : ```bash $ cd ...... 🐳 root ~/demo/a # $ gst On branch main Untracked files: notes.txt $ take /tmp/scratch 🐳 root /tmp/scratch # ``` `cd ......` (six points) remonte cinq dossiers parents d'un coup. `gst` affiche le fichier untracked `notes.txt` sans que vous tapiez `git status` — ça fonctionne depuis n'importe quelle profondeur du repo, puisque Git remonte tout seul jusqu'à trouver `.git`. `take` crée `/tmp/scratch` et vous y place. Appuyez sur `TAB` après `cd ` pour voir l'autocomplétion des dossiers en action. ## Installation {#installation} L'installation est assez simple, seulement trois commandes : ```bash $ sudo apt-get update && sudo apt-get install zsh $ chsh -s /usr/bin/zsh $ sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" ``` À la dernière instruction, on vous demandera si vous voulez faire de zsh votre shell par défaut ; répondez simplement `Y`es. Et très vite, vous voyez un changement dans votre console : ![ZSH a été installé](./images/zsh_install.webp) Le premier changement concerne le prompt. Là, je suis dans mon dossier blog et c'est un repository git. Oh-My-ZSH m'affiche donc cette info et, en plus, la branch sur laquelle je travaille (la branch `master` ici). Sympa, mais on peut faire beaucoup mieux. Il est temps d'installer un moteur de templates appelé Powerlevel10k (). Si vous préférez l'essayer dans un container jetable avant de toucher à votre machine, lisez [Personnalisez votre prompt Linux avec Powerlevel 10k](/blog/powerlevel10k_sandbox). Dans le paragraphe suivant, j'installe Powerlevel10k sur mon ordinateur, alors allons-y. ### Powerlevel10k {#powerlevel10k} L'installation est simple aussi, lancez juste `git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/themes/powerlevel10k` dans votre console. Vous téléchargerez ainsi le template. La deuxième action consiste à éditer le fichier `~/.zshrc`. Comme j'utilise VSCode, je le démarre, j'appuie sur `CTRL`+`O` et j'ouvre le fichier `~/.zshrc`. Cherchez `ZSH_THEME` et mettez la valeur `powerlevel10k/powerlevel10k`. ![Thème ZSH](./images/zsh_theme.webp) Dernière étape : fermez votre console Linux actuelle et ouvrez-en une nouvelle. La première fois, vous obtiendrez l'assistant de configuration de Powerlevel10k : ![Configuration de ZSH](./images/zsh_configuration.webp) Répondez à toutes les questions et, quand il s'agit de définir vos préférences, choisissez simplement l'option qui vous plaît le plus. ![ZSH - Définissez vos préférences](./images/zsh_preferences.webp) Une fois terminé, voici à quoi ressemble mon prompt : ![Powerlevel10k - Nouveau prompt](./images/powerlevel10k_prompt.webp) Et c'est tellement mieux (et bien plus joli). En un instant, je sais que je suis dans mon dossier `blog`, qu'il s'agit d'un repository Git, que je suis sur la branch `main` et `?1` signifie que j'ai un fichier *untracked* ou *modifié* sur mon ordinateur, pas encore poussé sur le repo central (GitHub ici). À droite, on voit que je suis connecté en tant que `root` sur ma machine. C'est affiché en rouge justement pour indiquer que j'ai les accès root. #### Personnaliser le prompt {#customizing-the-prompt} Bon, supprimons cette partie puisque, oui, c'est mon ordinateur personnel, il est normal que je sois root ici car je n'ai qu'un seul compte local — cette information n'a donc aucun intérêt pour moi. Powerlevel10k a une excellente documentation et en lisant [How do I add username and/or hostname to prompt?](https://github.com/romkatv/powerlevel10k?tab=readme-ov-file#how-do-i-add-username-andor-hostname-to-prompt), on apprend comment le retirer de la même manière. Éditez simplement le fichier `~/.p10k.zsh`, cherchez `POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS` puis trouvez son entrée `context` et commentez la ligne. Sauvegardez le fichier, ouvrez une nouvelle console et bingo, la partie droite du prompt ne contient plus le nom d'utilisateur — vous avez maintenant sous les yeux le prompt montré en haut de cet article. ## Quelques fonctionnalités que j'utilise tous les jours {#some-features-i-use-daily} Vous pourriez penser que ces fonctionnalités ne sont pas nécessaires, mais le fait est que je les utilise énormément dans mon travail quotidien. Par exemple, `take /tmp/new_folder` crée le dossier s'il n'existe pas encore et vous y place. Soyons honnêtes, `take /tmp/new_folder` est plus simple et plus rapide que `mkdir -p /tmp/new_folder && cd $_`, non ? > **Take n'est qu'une fonction personnalisée:** > > `take` est en fait une fonction définie dans le fichier `~/.oh-my-zsh/lib/functions.zsh`. Une deuxième fonctionnalité, tellement bête mais tellement précieuse : `cd ..` suivi de deux, trois ou ... points. Imaginez que vous soyez dans le dossier `/tmp/new_folder/a/b/c/d/e/f` et que vous vouliez utiliser `cd ..` pour revenir au parent `a`. Avec ZSH, je peux simplement taper `cd ......` et c'est génial. `cd ..` remonte au premier parent, `cd ...` au grand-parent et ainsi de suite. Il y a aussi une fonctionnalité clé de ZSH : `TAB`. Imaginez que vous soyez dans un dossier contenant plusieurs sous-dossiers. Tapez juste `cd ` (l'espace est vraiment important) et appuyez sur `TAB`. Vous obtenez alors la liste des sous-dossiers. Naviguez entre les suggestions avec `TAB` et appuyez sur `ENTER` pour en choisir un. ![ZSH - CD avec tab](./images/zsh_cd.webp) Puis continuez, appuyez encore sur `TAB` et vous pourrez sélectionner un sous-dossier, et ainsi de suite. ![ZSH - CD avec tab](./images/zsh_cd_again.webp) `TAB` est donc utilisé par ZSH pour l'autocomplétion. Essayons autre chose : `head -` suivi de `TAB`. Comme vous le voyez ci-dessous, j'obtiens la liste des flags que je peux utiliser, je peux naviguer toujours avec `TAB` et continuer à taper ma ligne de commande. ![ZSH - autocomplétion de la commande head](./images/zsh_head_autocompletion.webp) Côté alias, celui que j'utilise plusieurs fois par jour est `gst` pour `git status`. Vous pouvez retrouver la liste de tous les alias Git dans votre fichier `/root/.oh-my-zsh/plugins/git/git.plugin.zsh`. Un bon exemple est `gwip` qui commite tous vos changements en tant que `work in progress`, ce qui vous permet de mettre de côté votre travail en cours, de basculer sur une autre branch (par ex. pour travailler sur une issue) et, une fois terminé, de revenir sur votre branch de feature et de récupérer votre travail. Facile. Et bien sûr, il y en a d'autres — je ne sais probablement même plus qu'ils font partie de ZSH. ## Conclusion {#conclusion} Trois commandes installent Zsh et Oh-My-Zsh, un `git clone` et une ligne `ZSH_THEME` apportent le prompt Powerlevel10k, et une seule entrée commentée dans `~/.p10k.zsh` supprime le bruit inutile. Ensuite, les gains sont permanents : `take`, `cd ...`, la complétion avec `TAB` et `gst` se tapent des dizaines de fois par jour sans même y penser. Il y a encore beaucoup, beaucoup plus, comme les [plugins](/blog/tags/zsh). Prochaine étape : [zsh-autosuggestions](/blog/zsh-plugin-autosuggestions) et [zsh-syntax-highlighting](/blog/zsh-syntax-highlighting), deux incontournables une fois Oh-My-Zsh en place. --- # Autosuggestions dans la console avec ZSH > Boostez l'efficacité de votre console ZSH. Installez facilement le plugin zsh-autosuggestions et obtenez des suggestions de commandes intelligentes tirées de votre historique pendant que vous tapez. --- ![Autosuggestions dans la console avec ZSH](/img/v2/zsh.webp) **TL;DR** Cet article présente `zsh-autosuggestions`, un plugin Zsh qui suggère des commandes issues de l'historique de votre shell pendant que vous tapez, affichées en gris et acceptables avec `TAB`. Il explique comment installer le plugin en le clonant dans le dossier des plugins personnalisés d'Oh My Zsh et comment l'activer dans `~/.zshrc`, puis montre comment accepter, faire défiler ou ignorer les suggestions avec `TAB` et les flèches. ZSH supporte les plugins, et l'une des merveilles s'appelle [zsh-autosuggestions](https://github.com/zsh-users/zsh-autosuggestions) — en supposant que vous avez déjà installé Oh-My-Zsh (voir [How to install Oh-My-ZSH](/blog/zsh-install) sinon). Celui-là vous suggère des commandes pendant que vous tapez, sur base de votre historique et des complétions. Trois frappes, et la commande de quarante caractères que vous aviez péniblement assemblée le mois dernier est là, à attendre un `TAB`. ## Trois caractères, et la commande est déjà là {#three-characters-and-the-command-is-already-there} Imaginez que vous ayez déjà tapé, aujourd'hui, hier ou il y a des semaines, la commande `docker compose up --detach`. Aujourd'hui, vous tapez `doc` et voici ce que votre console affiche : ![Plugin autosuggestions pour ZSH](./images/autosuggestions.webp) La partie en gris, c'est la suggestion. Appuyez sur `TAB` et toute la ligne est à vous ; continuez à taper et la suggestion se met à jour à chaque caractère ; ignorez-la et il ne se passe rien. Vous pouvez aussi jouer avec `UP` et `DOWN` pour passer aux autres commandes correspondant aux mêmes touches (*doc* dans mon exemple). ## Pourquoi ça marche {#why-it-works} * Les suggestions viennent directement de votre HISTORY Linux : aucun index à construire, aucune configuration, rien à apprendre au plugin. * Plus vous utilisez votre console, meilleures sont les suggestions — il apprend de vous et de vous seul. * Fini le « Mince, c'était quoi déjà les paramètres que j'avais utilisés pour ... ». ## Le voir à l'œuvre avec Docker {#seeing-it-in-action-with-docker} Vous n'avez pas besoin d'avoir déjà Oh-My-Zsh sur votre machine pour voir le plugin fonctionner — un container jetable fait l'affaire. > **Pourquoi Docker d'abord ?:** > > Le Dockerfile ci-dessous installe Oh-My-Zsh, clone le plugin, l'active dans `~/.zshrc` et — c'est > ça qui compte — pré-remplit un historique de shell contenant `docker compose up --detach`. Tapez `doc` > et vous obtenez exactement le scénario de la capture ci-dessus, sans rien avoir à configurer. **Dockerfile** ```docker FROM ubuntu:24.04 ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update && \ apt-get install -y --no-install-recommends \ zsh \ git \ curl \ ca-certificates && \ rm -rf /var/lib/apt/lists/* # Install Oh-My-Zsh unattended (host framework required by the plugin) RUN sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" \ "" --unattended --keep-zshrc # Install zsh-autosuggestions and enable it in the default plugins=() list RUN git clone --depth=1 https://github.com/zsh-users/zsh-autosuggestions \ /root/.oh-my-zsh/custom/plugins/zsh-autosuggestions && \ sed -i 's/^plugins=(git)/plugins=(git zsh-autosuggestions)/' /root/.zshrc # Pre-seed a shell history so a suggestion is already there to accept: type # "doc" and the full `docker compose up --detach` line appears in gray text. RUN printf '%s\n' \ ': 1700000000:0;docker compose up --detach' \ ': 1700000100:0;docker compose logs --follow' \ ': 1700000200:0;docker compose down --volumes' \ > /root/.zsh_history # Signal clearly that this shell runs inside a container RUN echo "PROMPT='%B%F{yellow}🐳 %F{cyan}%n%f%b %~ %# '" >> /root/.zshrc CMD ["/bin/zsh", "-i", "-l"] ``` Buildez et lancez : ```bash $ docker build -t autosuggestions-demo . [+] Building 19.8s (8/8) FINISHED ✔ exporting to image $ docker run --rm -it autosuggestions-demo 🐳 root ~ # ``` Tapez maintenant `doc` (n'appuyez pas encore sur `ENTER`) et la scène exacte de la capture en haut de cet article se rejoue : `docker compose up --detach` apparaît en gris, comme un fantôme, juste après votre curseur — lu directement depuis l'historique pré-rempli. Appuyez sur `TAB` pour l'accepter, ou sur `↑`/`↓` pour passer aux deux autres commandes `docker compose` qui se trouvent elles aussi dans cet historique. ## Installation {#installation} Clonez simplement le repository officiel comme ceci : ```bash $ `git clone https://github.com/zsh-users/zsh-autosuggestions \${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions` ``` Éditez ensuite le fichier `~/.zshrc`, cherchez `plugins=(` et ajoutez `zsh-autosuggestions` à la liste. Vous aurez par exemple quelque chose comme ceci : **\~/.zshrc** ```bash plugins=( git # ... your existing plugins zsh-autosuggestions ) ``` Fermez votre console, ouvrez-en une nouvelle (ou lancez `source ~/.zshrc` pour charger votre modification) et c'est fait. ## Conclusion {#conclusion} Un `git clone`, une ligne dans `~/.zshrc`, et votre terminal arrête de vous demander de retenir des flags tapés il y a trois semaines. Comme tout est lu depuis votre historique, le plugin prend de la valeur chaque jour que vous l'utilisez. Puisque les suggestions ne valent que ce que vaut le contenu stocké, il est utile de savoir comment cet historique est conservé et filtré : voir [Linux - Working with the history of your last fired actions](/blog/linux-history). Et pour un plugin dans le même esprit, dédié à vos hosts SSH : [SSH - Autosuggestions with ZSH](/blog/zsh-plugin-ssh-config-suggestions). --- # La coloration syntaxique dans la console avec ZSH > Améliorez votre console Zsh avec la coloration syntaxique ! Les commandes passent au vert (valides) ou au rouge (invalides) pendant que vous tapez. Guide d'installation rapide pour zsh-syntax-highlighting. --- ![La coloration syntaxique dans la console avec ZSH](/img/v2/zsh.webp) **TL;DR** Cet article présente `zsh-syntax-highlighting`, un plugin Zsh qui colore les commandes dans votre terminal pendant que vous tapez : vert pour les commandes valides et exécutables, rouge pour les fautes de frappe ou les commandes inconnues. Il détaille le clonage du plugin dans le dossier des plugins personnalisés d'Oh My Zsh et son ajout à la liste `plugins=(...)` de `~/.zshrc`, sans aucune autre configuration pour commencer à l'utiliser. [zsh-syntax-highlighting](https://github.com/zsh-users/zsh-syntax-highlighting/) est une autre perle pour ZSH — à condition d'avoir déjà installé Oh-My-Zsh (voir [How to install Oh-My-ZSH](/blog/zsh-install) si ce n'est pas le cas). Pendant que vous tapez, les couleurs vous diront par exemple que quelque chose ne va pas. Si vous tapez `head` suivi d'un espace, ZSH affichera ce mot en vert : cette commande existe et elle est valide. Si vous tapez `heat`, le mot apparaîtra en rouge : cette commande n'existe pas. Ça paraît simple, mais c'est tellement pratique. ## À quoi ressemble la coloration syntaxique {#what-syntax-highlighting-looks-like} Tapez une commande comme `cat` ou `head` et elle passe au vert : la commande existe, elle est correctement écrite et elle est exécutable. ![Highlight in green](./images/head.webp) Faites une faute de frappe et le même mot passe au rouge avant même d'appuyer sur `ENTER` : ![Highlight in red](./images/docker_compose.webp) Cette couleur apparaît **pendant que vous tapez**, pas après l'échec de la commande. Vous savez que vous avez mal écrit `docekr` avant de perdre une seconde avec un `command not found`. ## Comment l'utiliser {#how-to-use-it} En réalité, il n'y a rien à faire : pas de fichier de configuration, aucune option à régler, aucun alias à définir. Installez le plugin, rechargez votre shell, et la coloration est là dès le prochain caractère tapé. ## Le voir à l'œuvre avec Docker {#seeing-it-in-action-with-docker} Pas d'historique à alimenter, pas de config à écrire — ce plugin n'a besoin de rien d'autre que lui-même, ce qui en fait un test Docker parfait en une seule commande. > **Pourquoi Docker d'abord ?:** > > Le Dockerfile ci-dessous installe Oh-My-Zsh et le plugin, puis l'active dans `~/.zshrc` — exactement > les deux étapes de la section « Installation », faites pour vous. Rien ne change sur votre machine. **Dockerfile** ```docker FROM ubuntu:24.04 ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update && \ apt-get install -y --no-install-recommends \ zsh \ git \ curl \ ca-certificates && \ rm -rf /var/lib/apt/lists/* # Install Oh-My-Zsh unattended (host framework required by the plugin) RUN sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" \ "" --unattended --keep-zshrc # Install zsh-syntax-highlighting and enable it in the default plugins=() list RUN git clone --depth=1 https://github.com/zsh-users/zsh-syntax-highlighting \ /root/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting && \ sed -i 's/^plugins=(git)/plugins=(git zsh-syntax-highlighting)/' /root/.zshrc # Signal clearly that this shell runs inside a container RUN echo "PROMPT='%B%F{yellow}🐳 %F{cyan}%n%f%b %~ %# '" >> /root/.zshrc CMD ["/bin/zsh", "-i", "-l"] ``` Buildez et lancez-le : ```bash $ docker build -t syntax-demo . [+] Building 18.4s (7/7) FINISHED ✔ exporting to image $ docker run --rm -it syntax-demo 🐳 root ~ # head ``` Tapez `head` et le mot passe au vert dès que vous finissez de l'écrire — la commande existe. Effacez-le, tapez `heat` ou `docekr` à la place, et il passe au rouge avant même d'appuyer sur `ENTER` : la même scène que sur les deux captures ci-dessus, en direct dans votre propre terminal. ## Installation {#installation} Il suffit de cloner le repository officiel comme ceci : ```bash $ `git clone https://github.com/zsh-users/zsh-syntax-highlighting \${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting` ``` Éditez ensuite le fichier `~/.zshrc`, cherchez `plugins=(` et ajoutez `zsh-syntax-highlighting` à la liste. Vous aurez par exemple quelque chose comme ceci : **\~/.zshrc** ```bash plugins=( git # ... your existing plugins zsh-syntax-highlighting ) ``` Fermez votre console, ouvrez-en une nouvelle (ou lancez `source ~/.zshrc` pour charger votre modification) et c'est terminé. ## Conclusion {#conclusion} Deux lignes de configuration et votre terminal se met à vous relire : le vert veut dire « ça va tourner », le rouge « corrige-moi d'abord ». C'est le genre de petite boucle de feedback qu'on ne remarque plus après une semaine — jusqu'au jour où vous vous asseyez devant une machine qui n'en a pas et où vous tapez à l'aveugle. Son compagnon naturel est [Autosuggestions in the console using ZSH](/blog/zsh-plugin-autosuggestions) : celui-ci vous dit si ce que vous tapez est valide, l'autre vous évite de le taper tout court. --- # Linux - Travailler avec l'historique de vos dernières commandes > Maîtrisez l'historique de votre ligne de commande Linux. Apprenez à afficher, localiser (.bash_history, .zsh_history) et gérer vos commandes précédemment exécutées grâce à quelques astuces essentielles. --- ![Linux - Travailler avec l'historique de vos dernières commandes](/img/v2/linux_tips.webp) **TL;DR** Cet article traite de l'historique du shell sous Bash/ZSH : localiser le fichier d'historique via `$HISTFILE`, exclure les commandes sensibles avec `HISTIGNORE`/`HISTORY_IGNORE` ou une espace en début de ligne (`hist_ignore_space`), filtrer avec `history | grep`, faire une recherche floue via `CTRL`+`R`, utiliser zsh-autosuggestions pour éviter complètement la recherche manuelle, et supprimer des entrées précises avec `history -d`. Pour moi, l'une des forces indéniables de la ligne de commande sous Linux, c'est la gestion de l'historique des commandes déjà exécutées. *La plus grosse amélioration que vous pouvez apporter à cet historique, c'est [fzf](/blog/linux-fzf-introduction) : il transforme `CTRL`+`R` en une recherche floue et interactive au lieu d'une recherche linéaire. Si vous voulez quelque chose de plus structuré — avec horodatage, codes de sortie, répertoire de travail et synchronisation multi-machines en option — [Atuin](/blog/atuin-bash-history) remplace complètement le fichier d'historique plat par une base de données SQLite.* Pouvoir appuyer sur les touches `Haut` ou `Bas` du clavier pour revenir aux commandes lancées précédemment, waouh. Pour les plus anciens, il y avait [DOSKEY](https://en.wikipedia.org/wiki/DOSKEY) sous MSDos, mais Linux va beaucoup plus loin. Voyons quelques astuces. ## Obtenir votre historique {#getting-your-history} Commençons par le début : pour afficher la liste des commandes que vous avez saisies par le passé, lancez simplement `history` dans une console Linux. ## Emplacement de votre historique {#location-of-your-history} Par défaut, votre historique est stocké dans un fichier appelé `.bash_history` dans votre répertoire personnel. Vous pouvez afficher les cinq dernières commandes, par exemple, en lançant `tail -n5 ~/.bash_history`. > **De mon côté, comme j'utilise ZSH, mon historique se trouve dans `~/.zsh_history`.:** > > Le moyen le plus simple de savoir où votre historique est stocké est d'afficher le contenu de la variable `$HISTFILE` comme ceci : `echo $HISTFILE`. > **Information:** > > Très récemment, j'ai dû reprendre un script développé par un collègue (appelons-le JohnDoe) et je ne savais pas comment l'appeler depuis la ligne de commande ni quels paramètres lui passer. > > Comme je suis *root user* sur le serveur, j'ai simplement consulté son historique. Il m'a suffi d'afficher son fichier `/home/john_doe/.bash_history`. ### Variables HISTIGNORE // HISTORY\_IGNORE {#histignore--history\_ignore-variables} La variable `HISTIGNORE` (pour Bash) ou `HISTORY_IGNORE` (ZSH) permet de spécifier des motifs que vous ne voulez pas voir enregistrés dans votre fichier d'historique. Prenons un exemple : `HISTIGNORE="sudo *"` empêchera toutes les commandes commençant par `sudo` d'être écrites dans le fichier d'historique. Vous pouvez aussi utiliser un motif comme `HISTIGNORE="*password*"` pour éviter que les commandes contenant le mot *password* soient écrites. Pour pouvoir spécifier plus d'une règle, utilisez le caractère deux-points `:` comme dans `HISTIGNORE="sudo *:*password*"`. ## Ne pas mettre une commande précise dans l'historique {#dont-put-a-specific-command-in-the-history} Imaginez une commande comme `curl --user name:password http://www.example.com`. Vouliez-vous vraiment mettre cette commande (donc votre login et votre mot de passe en clair) dans le fichier d'historique ? Avec [ZSH](/blog/tags/zsh), il me suffit d'ajouter une espace avant ma commande, donc en lançant `[SPACE]curl --user name:password http://www.example.com`. ```none # So, don't start your command like this: $ curl --user name:password http://www.example.com # v--- But just add a space character before $ curl --user name:password http://www.example.com ``` > **Information:** > > Le caractère espace est pris en charge par l'option `hist_ignore_space`, définie dans `.oh-my-zsh/lib/history.zsh`. ## Utiliser grep pour filtrer votre historique {#using-grep-to-filter-your-history} Disons que vous voulez retrouver les dernières instructions `docker exec` que vous avez lancées : `history | grep -i "docker exec"`, c'est-à-dire simplement envoyer via un *pipe* la sortie de la commande `history` vers `grep` et appliquer un filtre (insensible à la casse grâce au flag `-i`). De mon côté, je n'en ai pas besoin parce que j'utilise `CTRL`+`R` sous ZSH. ## CTRL-R est votre ami {#ctrl-r-is-your-friend} Les touches `CTRL`+`R` affichent une petite fenêtre popup (sous Bash comme sous ZSH) et vous pourrez rechercher rapidement dans l'historique de vos commandes. Voici l'apparence sous ZSH ; bien plus agréable et plus simple que sous Bash. ![CTRL-R dans ZSH](./images/ctrl_r.webp) Appuyez donc sur `CTRL`+`R` puis commencez à taper quelques lettres comme *gacom* et chaque entrée contenant ces lettres (comme **g**it add . ; git **com**mit ...) sera retrouvée. Ce popup reste toutefois un parcours linéaire dans un fichier texte plat : pas d'horodatage, pas de code de sortie, aucune idée du dossier dans lequel vous étiez. Si cette limitation commence à vous gêner, [Atuin reconstruit le même raccourci `CTRL`+`R` au-dessus d'une base de données SQLite](/blog/atuin-bash-history) et conserve tout ce contexte pour vous. ## Autosuggestions dans la console avec ZSH {#autosuggestions-in-the-console-using-zsh} En utilisant ZSH et zsh-autosuggestion (voir [mon article précédent](/blog/zsh-plugin-autosuggestions)), vous n'avez même plus à vous soucier de l'historique. Commencez juste à taper les premières lettres d'une commande comme **doc** et toutes vos instructions **docker** précédentes seront accessibles (utilisez les touches de navigation pour sélectionner une commande précédente ou tapez quelques lettres de plus pour affiner). ## Supprimer une instruction de votre historique {#delete-an-instruction-from-your-history} Aïe ! Vous avez oublié d'ajouter une espace avant votre instruction confidentielle et, du coup, elle est maintenant stockée dans l'historique. Pas de panique ! Le fichier d'historique est... un fichier, donc lancez simplement `vi $HISTFILE` et vous pouvez l'éditer. Ou, peut-être plus simple, utilisez `history -d` suivi du numéro de ligne tel qu'affiché dans la sortie de l'historique. Donc si la sortie de `history` est celle ci-dessous, je peux supprimer la ligne `vi` en lançant `history -d 2130` ```bash $ history -d 2130 2129 history | grep -i "docker exec" 2130 vi $HISTFILE 2131 history | grep -i "docker exec" ``` > **Information:** > > Mais, ensuite, la commande `history -d 2130` apparaîtra dans l'historique, évidemment. Éditer le fichier avec un éditeur de texte est donc peut-être plus... discret. > **Note:** > > Vous pouvez spécifier une plage comme `history -d 2100-2130` (Bash 5.1+). --- # Fonctions ZSH - Personnaliser votre shell pour gérer Docker > Une collection de fonctions ZSH pour améliorer votre expérience du terminal, avec la gestion interactive des containers Docker grâce à fzf. Ouvrez de nouvelles sessions, arrêtez des containers et bien plus, sans effort. --- ![Fonctions ZSH - Personnaliser votre shell pour gérer Docker](/img/v2/zsh.webp) **TL;DR** Améliorez votre workflow dans le terminal avec des fonctions ZSH personnalisées pour gérer Docker de façon interactive. Cet article présente des outils comme `dex`, `dstop` et `dnuke` qui s'appuient sur `fzf` pour accéder rapidement à un shell, arrêter des containers, consulter les logs et nettoyer les ressources, sans rien mémoriser. Au quotidien, j'utilise quelques fonctions ZSH que je trouve bien pratiques. Ces fonctions peuvent être ajoutées à votre fichier `~/.zshrc` (ou, mieux, dans leur propre fichier — voir [Beyond the Monolith - Organizing Your ZSH Workflow Like a Pro](/blog/modular-zsh-workflow)) pour améliorer votre expérience du terminal quand vous travaillez avec Docker. Elles offrent des moyens rapides et interactifs de gérer vos containers Docker **sans devoir retenir des commandes précises ou des ID de containers** (et soyons honnêtes, on les oublie vite !). Dans cet article, je partage quelques-unes des fonctions ZSH que j'utilise pour gérer Docker : ouvrir une nouvelle session de terminal dans un container en cours d'exécution, arrêter des containers, accéder aux logs (même si le container est arrêté) et nettoyer les ressources inutilisées. Ces fonctions s'appuient sur `fzf` pour une sélection interactive, ce qui rend la gestion de votre environnement Docker bien plus simple, directement depuis votre terminal. **Quick Jump:** [Ouvrir une nouvelle session de terminal dans un container Docker en cours d'exécution](#start-a-new-terminal-session-in-a-running-docker-container) | [Comment l'installer](#how-to-install) Comme ces fonctions reposent sur `fzf`, si vous ne l'avez pas installé, faites-le via votre gestionnaire de packages (par exemple `sudo apt-get install fzf` sur les systèmes basés sur Debian). Vous découvrez l'outil ? Commencez par lire [Introduction to fzf - Fuzzy Finder](/blog/linux-fzf-introduction). ## Ouvrir une nouvelle session de terminal dans un container Docker en cours d'exécution {#start-a-new-terminal-session-in-a-running-docker-container} Imaginez que vous voulez accéder rapidement à un container Docker en cours d'exécution sans devoir retenir son nom ou son ID. La fonction `dex` fait exactement cela. Elle liste tous les containers en cours d'exécution et vous laisse en choisir un pour y ouvrir une nouvelle session de terminal. Il suffit donc de lancer `dex` dans votre terminal pour accéder à n'importe lequel de vos containers Docker actifs. C'est particulièrement utile quand plusieurs containers tournent en même temps et que vous voulez sauter dans l'un d'eux sans chercher son nom ou son ID. ![Utiliser dex pour ouvrir une nouvelle session de terminal dans un container Docker en cours d'exécution](./images/dex.webp) > **Modifier la commande avant de l'exécuter:** > > Appuyez sur Entrée pour sélectionner le container et démarrer la session, ou sur Ctrl+E pour récupérer la commande dans votre prompt et la modifier — pratique si vous voulez l'ajuster avant de la lancer, par exemple pour désactiver l'entrypoint ou monter un volume. > **Accès root:** > > Appuyez sur `Ctrl`+`R` pendant la sélection d'un container avec `dex` pour ouvrir la session en root. Utile quand vous devez effectuer des tâches d'administration dans le container qui exigent des privilèges élevés. > **Filtrage:** > > Lancez `dex any_pattern` pour ne garder dans la liste que les containers correspondant à ce motif. ## Arrêter un ou plusieurs containers Docker en cours d'exécution {#stop-one-or-more-running-docker-containers} `dstop` est une autre fonction bien utile : elle permet d'arrêter un ou plusieurs containers Docker actifs. Comme `dex`, elle utilise `fzf` pour offrir une interface interactive de sélection des containers à arrêter. Vous pouvez en sélectionner plusieurs grâce à la multi-sélection de `fzf`, c'est-à-dire en appuyant sur `Tab` pour cocher plusieurs containers avant d'appuyer sur `Enter` pour les arrêter. Appuyez sur `CTRL`+`A` pour sélectionner tous les containers d'un coup. Dans le panneau de droite, vous voyez les informations du ou des containers sélectionnés : nom, image, statut et ports. Cela vous aide à décider en connaissance de cause lesquels arrêter. Vous voyez aussi la consommation de ressources en direct (CPU, mémoire, I/O réseau, I/O disque), de quoi repérer les containers les plus gourmands avant de les arrêter. ![Utiliser dstop pour arrêter un ou plusieurs containers Docker en cours d'exécution](./images/dstop.webp) ## Accéder aux logs d'un container Docker en cours d'exécution {#access-logs-of-a-running-docker-container} La fonction `dlogs` permet d'accéder aux logs d'un container Docker actif. Elle utilise également `fzf` pour proposer une interface interactive de sélection du container dont vous voulez voir les logs. Dans le panneau de droite, vous voyez les logs du container sélectionné en temps réel. Très pratique pour le debug : vous suivez les logs au fur et à mesure. La molette de votre souris permet de les faire défiler. ![La fonction dlogs pour accéder aux logs d'un container Docker en cours d'exécution](./images/dlogs.webp) ## Nettoyer les containers, images, volumes et réseaux Docker inutilisés {#clean-up-unused-docker-containers-images-volumes-and-networks} La fonction dnuke joue le rôle d'un **assistant de nettoyage Docker** intelligent et interactif, conçu pour récupérer de l'espace disque sans risquer de supprimer des ressources à l'aveugle. Contrairement à un simple `docker system prune`, `dnuke` commence par analyser votre environnement et vous présente un **plan d'exécution** dynamique, qui montre exactement ce qui peut être nettoyé (containers arrêtés, images dangling, volumes inutilisés et cache de build). Le nettoyage se fait par étapes fines, ce qui vous permet d'ignorer certaines catégories si besoin. Par défaut, la fonction reste en mode « safe » et ne cible que les images dangling (sans tag). `dnuke` accepte des flags CLI pour adapter son comportement : **Les flags de dnuke** * **Nettoyage en profondeur** — `dnuke -a` (ou `--all`) supprime **toutes** les images inutilisées, pas seulement les dangling (comme `docker image prune -a`). * **Non interactif** — `dnuke -y` (ou `--force`) court-circuite l'assistant et confirme automatiquement toutes les étapes. * **Aide** — `dnuke --help` affiche la liste complète des options et des exemples d'utilisation. Si l'environnement est déjà propre, l'assistant le détecte immédiatement et s'arrête sans poser de questions inutiles. ![Afficher l'aide de dnuke pour voir les options disponibles](./images/dnuke_help.webp) ![Lancer dnuke pour nettoyer les ressources Docker inutilisées](./images/dnuke.webp) ## Créer un nouveau container {#creating-a-new-container} La dernière fonction est `drun` : elle affiche la liste des images existantes et crée un nouveau container à partir de celle que vous choisissez. > **Filtrage:** > > Lancez `drun any_pattern` pour ne garder dans la liste que les images correspondant à ce motif. ## Une seule pour les gouverner toutes {#one-to-rule-them-all} La fonction `dops` est un wrapper autour des autres fonctions. Elle vous donne accès rapidement à toutes les autres avec une seule commande. Lancez simplement `dops` dans votre terminal : la liste des fonctions disponibles s'affiche. Vous choisissez celle qui vous intéresse et elle exécute la commande correspondante. Éditez maintenant votre fichier `~/.zshrc` et ajoutez la ligne suivante pour sourcer le fichier `docker.zsh` : `[[ -f ~/.zsh/docker.zsh ]] && source ~/.zsh/docker.zsh`. Les fonctions seront alors disponibles dans votre terminal. ![Utiliser dops pour accéder au menu interactif des fonctions de gestion Docker](./images/dops.webp) > **Quand vous ne savez pas encore quel container:** > > `dex`, `dstop` et `dlogs` supposent que vous savez déjà quel container vous visez. Pour les moments où ce n'est pas le cas — cinq containers viennent de démarrer et il vous faut voir CPU, mémoire et logs côte à côte pour identifier le fautif — un dashboard vaut mieux qu'un menu. C'est le rôle de [lazydocker, lancé dans un container](/blog/lazydocker). ## Comment l'installer {#how-to-install} Éditez votre fichier `~/.zshrc` et ajoutez les lignes suivantes à la fin : ```zsh fpath=(~/.zsh/docker-fns $fpath) autoload -Uz _d_check_env _d_print_cmd _d_has_running_containers autoload -Uz dex dlogs dnuke dops drun dstop # Setup alias for the interactive menu alias d='dops' # Print quick reference menu only in interactive sessions to avoid breaking scripts if [[ -o interactive ]]; then echo -e "\033[1;30mDocker Utils loaded:\033[0m" echo -e " \033[0;36mdex\033[0m (exec/root) \033[0;36mdlogs\033[0m (logs/json) \033[0;36mdstop\033[0m (stop/kill)" echo -e " \033[0;36mdnuke\033[0m (clean) \033[0;36mdops\033[0m (menu/alias \033[1;32md\033[0m) \033[0;36mdrun\033[0m (create new container)" fi ``` Créez ensuite le répertoire `~/.zsh/docker-fns` et déplacez-y les fichiers de chaque fonction (`dex`, `dlogs`, `dnuke`, `dops`, `drun`, `dstop`). Vos fonctions restent ainsi organisées et faciles à maintenir — le même pattern `fpath`/`autoload` est détaillé dans [Beyond the Monolith - Organizing Your ZSH Workflow Like a Pro](/blog/modular-zsh-workflow). **Project: \~/.zsh/docker-fns** > **Note:** > > Éditez maintenant votre fichier `~/.zshrc` et ajoutez les lignes mentionnées plus haut dans l'article pour sourcer le fichier `docker.zsh` et rendre les fonctions disponibles dans votre terminal. Vous pourrez alors utiliser `dex`, `dlogs`, `dnuke`, `dops`, `drun` et `dstop` pour gérer vos containers Docker directement depuis le terminal. **\_d\_check\_env** ``` #!/usr/bin/env zsh # Check for binary dependencies and Docker daemon status local dep for dep in "$@"; do if ! command -v "$dep" >/dev/null 2>&1; then echo >&2 -e "\033[0;31mError:\033[0m '$dep' is not installed." return 1 fi done # Fast daemon check to ensure Docker is responsive if ! docker info >/dev/null 2>&1; then echo >&2 -e "\033[0;31mError:\033[0m Docker daemon is not running or accessible." return 1 fi ``` **\_d\_print\_cmd** ``` #!/usr/bin/env zsh # Print executed commands with optional color formatting local cmd_str="$1" local color="${2:-\033[1;32m}" # Default to green local reset_color="\033[0m" # Truncate output if it exceeds 200 characters for readability if [[ ${#cmd_str} -gt 200 ]]; then cmd_str="${cmd_str:0:197}..." fi echo -e "Command: ${color}${cmd_str}${reset_color}" ``` **\_d\_has\_running\_containers** ``` #!/usr/bin/env zsh # Verify if at least one container is currently running # Utilizing 'read' is faster than piping to 'grep' if [[ -z "$(docker ps -q -n 1)" ]]; then echo >&2 -e "\033[0;33mInfo:\033[0m No running containers found." return 1 fi return 0 ``` **dex** ``` #!/usr/bin/env zsh # Interactive Docker Exec utilizing fzf _d_check_env fzf docker || return 1 _d_has_running_containers || return 0 local fzf_out key selection container_id cmd_exec user_flag query # Multi-line preview command using Docker Go templates # We use literal newlines here to ensure proper formatting inside fzf's subshell. # It extracts standard info, health state, ports, and the Docker Compose working directory. local preview_cmd='docker inspect --format "Container: {{.Name}} ---------------------------------------- Status: {{.State.Status}} Health: {{if .State.Health}}{{.State.Health.Status}}{{else}}No Healthcheck{{end}} Image: {{.Config.Image}} IP Address: {{range .NetworkSettings.Networks}}{{.IPAddress}} {{end}} Ports: {{range \$p, \$conf := .NetworkSettings.Ports}}{{\$p}} {{end}} Compose Dir: {{index .Config.Labels \"com.docker.compose.project.working_dir\"}}" {1}' # Removed the --height parameter to allow a full-screen fzf experience fzf_out=$(docker ps --format "{{.ID}}\t{{.Names}}\t{{.Image}}" | \ fzf --layout=reverse --border --query="$query" \ --header "Enter: Run | Ctrl-E: Edit | Ctrl-R: Run as Root" \ --preview "$preview_cmd" --preview-window=right:50%:wrap \ --expect=ctrl-e,ctrl-r) # Exit if user canceled [[ -z "$fzf_out" ]] && return 0 key=$(head -n 1 <<< "$fzf_out") selection=$(tail -n +2 <<< "$fzf_out") container_id=$(awk '{print $1}' <<< "$selection") [[ -z "$container_id" ]] && return 0 # Toggle root user execution based on keypress user_flag="" if [[ "$key" == "ctrl-r" ]]; then user_flag="--user root" fi # Smart shell execution: attempts zsh, then bash, fallbacks to sh local smart_shell="sh -c 'exec \$(command -v zsh || command -v bash || echo sh)'" cmd_exec=$(echo "docker exec -it ${user_flag} ${container_id} ${smart_shell}" | tr -s ' ') # Place command in buffer for editing or execute immediately if [[ "$key" == "ctrl-e" ]]; then print -z "$cmd_exec" else _d_print_cmd "$cmd_exec" "\033[1;36m" eval "$cmd_exec" fi ``` **dlogs** ``` #!/usr/bin/env zsh # Interactive Docker Logs Viewer _d_check_env fzf docker || return 1 local fzf_out key selection container_id cmd_logs bat_cmd # Determine the best available syntax highlighter (batcat for Debian/Ubuntu) if command -v batcat >/dev/null 2>&1; then bat_cmd="batcat" elif command -v bat >/dev/null 2>&1; then bat_cmd="bat" fi # Define the base preview command for container metadata local preview_cmd='docker inspect --format "Container: {{.Name}} Status: {{.State.Status}} Compose Dir: {{index .Config.Labels \"com.docker.compose.project.working_dir\"}}" {1} echo "------------------------------------------------------------" ' # Append the log fetching command to the preview with color support if [[ -n "$bat_cmd" ]]; then # Use bat for rich, out-of-the-box log syntax highlighting preview_cmd+="docker logs --tail 50 {1} 2>&1 | $bat_cmd --language=log --color=always --style=plain" else # Fallback to awk to manually colorize ERROR/FATAL (red) and WARN/WARNING (yellow) local awk_colors='{gsub(/ERROR|FATAL|ERR/,"\033[1;31m&\033[0m"); gsub(/WARNING|WARN/,"\033[1;33m&\033[0m"); print}' preview_cmd+="docker logs --tail 50 {1} 2>&1 | awk '$awk_colors'" fi # Full-screen fzf experience with an expanded right panel. # CRITICAL: --ansi is required to render color codes inside the preview window. fzf_out=$(docker ps -a --format "{{.ID}}\t{{.Names}}\t{{.Status}}\t{{.Image}}" | \ fzf --ansi --layout=reverse --border \ --header "Enter: View Logs | Ctrl-E: Edit Command" \ --preview "$preview_cmd" --preview-window=right:60%:wrap \ --expect=ctrl-e) # Exit if user canceled [[ -z "$fzf_out" ]] && return 0 key=$(head -n 1 <<< "$fzf_out") selection=$(tail -n +2 <<< "$fzf_out") container_id=$(awk '{print $1}' <<< "$selection") [[ -z "$container_id" ]] && return 0 # Construct log command based on available tools (Priority: lnav > bat > standard) if command -v lnav >/dev/null 2>&1; then cmd_logs="docker logs -f --tail 500 ${container_id} 2>&1 | lnav" elif [[ -n "$bat_cmd" ]]; then cmd_logs="docker logs -f --tail 500 ${container_id} 2>&1 | ${bat_cmd} --language=log --style=plain --pager=\"less +F -R\"" else cmd_logs="docker logs -f --tail 500 ${container_id}" fi # Place command in buffer for editing or execute immediately if [[ "$key" == "ctrl-e" ]]; then print -z "$cmd_logs" else _d_print_cmd "$cmd_logs" "\033[1;36m" eval "$cmd_logs" fi ``` **dnuke** ``` #!/usr/bin/env zsh # Docker Cleanup Wizard _d_check_env docker || return 1 local force=0 local prune_all=0 # Function to display usage instructions local _show_help() { echo -e "\033[1;34mUsage: dnuke [OPTIONS]\033[0m" echo -e "Interactive Docker cleanup wizard to remove unused resources." echo -e "" echo -e "\033[1;33mOptions:\033[0m" echo -e " \033[1;32m-a, --all\033[0m Deep clean: Remove ALL unused images (not just dangling)." echo -e " \033[1;32m-y, --force\033[0m Non-interactive mode: Auto-confirm all steps." echo -e " \033[1;32m-h, --help\033[0m Show this help message." echo -e "" echo -e "\033[1;33mExamples:\033[0m" echo -e " dnuke # Standard cleanup (dangling images only)" echo -e " dnuke -a # Deep cleanup (all unused images)" echo -e " dnuke -y # Instant cleanup (no questions asked)" } # Parse arguments while [[ $# -gt 0 ]]; do case $1 in -y|--force) force=1; shift ;; -a|--all) prune_all=1; shift ;; -h|--help) _show_help; return 0 ;; *) echo -e "\033[0;31mUnknown option: $1\033[0m"; _show_help; return 1 ;; esac done echo -e "\033[1;34m=== Docker Cleanup Wizard ===\033[0m" echo -n "Scanning Docker environment... " # 1. Gather data silently local c_out v_out cache_size c_out=$(docker ps -a -f "status=exited" -q) v_out=$(docker volume ls -qf dangling=true) cache_size=$(docker system df | awk '/Build Cache/ {print $5}') # $5 usually holds the size # Safely populate containers and volumes arrays local -a c_ids v_ids i_ids [[ -n "$c_out" ]] && c_ids=("${(@f)c_out}") [[ -n "$v_out" ]] && v_ids=("${(@f)v_out}") local s_cont="pending" s_img="pending" s_vol="pending" s_cache="pending" [[ ${#c_ids[@]} -eq 0 ]] && s_cont="none" [[ ${#v_ids[@]} -eq 0 ]] && s_vol="none" [[ "$cache_size" == "0B" || "$cache_size" == "0" || -z "$cache_size" ]] && s_cache="none" local img_desc="" local img_cmd="" # Handle image logic: Dangling vs All Unused if [[ $prune_all -eq 1 ]]; then # Mode: --all # Logic: Get ALL image IDs, subtract IDs used by containers (running or stopped) local -a all_imgs=("${(@f)$(docker images -q)}") local -a used_imgs=() # Get images used by containers local c_all_imgs c_all_imgs=$(docker ps -a --format "{{.Image}}") if [[ -n "$c_all_imgs" ]]; then # We need to resolve image names to IDs to perform accurate subtraction # (This might be slow on huge systems, but accurate) used_imgs=("${(@f)$(docker inspect --format='{{.Id}}' ${(f)c_all_imgs} 2>/dev/null | sed 's/sha256://' | cut -c1-12)}") fi # Zsh Array Subtraction: i_ids = all_imgs - used_imgs i_ids=(${all_imgs:|used_imgs}) [[ ${#i_ids[@]} -eq 0 ]] && s_img="none" img_desc="Remove ${#i_ids[@]} unused image(s) (Deep Clean)" img_cmd="docker image prune -a -f" else # Mode: Standard (Dangling only) local i_out=$(docker images -f "dangling=true" -q) [[ -n "$i_out" ]] && i_ids=("${(@f)i_out}") [[ ${#i_ids[@]} -eq 0 ]] && s_img="none" img_desc="Remove ${#i_ids[@]} dangling image(s)" img_cmd="docker rmi ${i_ids[*]}" fi echo -e "Done.\n" # Helper function to display the dynamic roadmap (Execution Plan) _draw_plan() { local current_step=$1 echo -e "\033[1;36m[ Execution Plan ]\033[0m" local _fmt() { local step_name=$1 local state=$2 local desc=$3 local prefix=" " local color="\033[1;30m" # Dark Gray local step_status="[Pending]" if [[ "$step_name" == "$current_step" ]]; then prefix="➜ " color="\033[1;37m" # White (Current step) step_status="[Next] " elif [[ "$state" == "done" ]]; then color="\033[1;32m" # Green (Finished) step_status="[Done] " elif [[ "$state" == "skipped" ]]; then color="\033[0;33m" # Yellow (Skipped manually) step_status="[Skipped]" elif [[ "$state" == "none" ]]; then color="\033[1;30m" # Dark Gray (Nothing to do) step_status="[Empty] " fi echo -e "${prefix}${color}${step_status} ${desc}\033[0m" } _fmt "cont" "$s_cont" "Remove ${#c_ids[@]} stopped container(s)" _fmt "img" "$s_img" "$img_desc" _fmt "vol" "$s_vol" "Remove ${#v_ids[@]} unused volume(s)" _fmt "cache" "$s_cache" "Prune build cache (Size: ${cache_size:-0B})" echo "----------------------------------------" } # Fast-exit if the environment is already clean if [[ "$s_cont" == "none" && "$s_img" == "none" && "$s_vol" == "none" && "$s_cache" == "none" ]]; then _draw_plan "finish" echo -e "\033[1;32mEnvironment is already clean. Nothing to do!\033[0m" return 0 fi # Helper function to handle user prompts consistently local _ask() { if [[ $force -eq 1 ]]; then return 0; fi if read -q "?Execute this step? (y/N): "; then echo "\n" return 0 else echo -e "\n\033[0;33mSkipped.\033[0m\n" return 1 fi } # --- Step 1: Stopped Containers --- if [[ "$s_cont" == "pending" ]]; then _draw_plan "cont" docker ps -a -f "status=exited" --format "table {{.Names}}\t{{.Status}}\t{{.Image}}" echo "" _d_print_cmd "docker rm ${c_ids[*]}" "\033[1;35m" if _ask; then docker rm "${c_ids[@]}" >/dev/null && echo -e "\033[1;32mContainers removed successfully.\033[0m\n" s_cont="done" else s_cont="skipped" fi fi # --- Step 2: Images (Dangling or All Unused) --- if [[ "$s_img" == "pending" ]]; then _draw_plan "img" if [[ $prune_all -eq 1 ]]; then # Warning specific to --all mode echo -e "\n\033[0;33mWarning: This will remove ALL images not currently used by a container.\033[0m" _d_print_cmd "$img_cmd" "\033[1;35m" if _ask; then eval "$img_cmd" >/dev/null && echo -e "\033[1;32mImages removed successfully.\033[0m\n" s_img="done" else s_img="skipped" fi else # Standard mode docker images -f "dangling=true" echo "" _d_print_cmd "$img_cmd" "\033[1;35m" if _ask; then docker rmi "${i_ids[@]}" >/dev/null && echo -e "\033[1;32mImages removed successfully.\033[0m\n" s_img="done" else s_img="skipped" fi fi fi # --- Step 3: Dangling Volumes --- if [[ "$s_vol" == "pending" ]]; then _draw_plan "vol" docker volume ls -f dangling=true echo "" _d_print_cmd "docker volume rm ${v_ids[*]}" "\033[1;35m" if _ask; then docker volume rm "${v_ids[@]}" >/dev/null && echo -e "\033[1;32mVolumes removed successfully.\033[0m\n" s_vol="done" else s_vol="skipped" fi fi # --- Step 4: Build Cache --- if [[ "$s_cache" == "pending" ]]; then _draw_plan "cache" local prune_cmd="docker builder prune -f" if docker buildx version >/dev/null 2>&1; then prune_cmd="docker buildx prune -f" fi _d_print_cmd "$prune_cmd" "\033[1;35m" if _ask; then eval "$prune_cmd" >/dev/null && echo -e "\033[1;32mCache pruned successfully.\033[0m\n" s_cache="done" else s_cache="skipped" fi fi # Final recap _draw_plan "finish" echo -e "\033[1;32mCleanup Wizard completed.\033[0m" ``` **dops** ``` #!/usr/bin/env zsh # Interactive Menu for Docker Operations _d_check_env fzf || return 1 local -a options=( "dex: Exec into a container (smart shell & root toggle)" "dlogs: View logs (smart viewer & JSON support)" "dnuke: Cleanup wizard (BuildKit aware)" "drun: Run a new ephemeral container from an image" "dstop: Batch stop or kill containers" ) local selected_op selected_op=$(print -l "${options[@]}" | \ fzf --height 30% --layout=reverse --header "Select Docker Operation" --border | \ awk -F':' '{print $1}') [[ -z "$selected_op" ]] && return 0 # Verify the selected function exists in the current Zsh session before executing if type "$selected_op" >/dev/null 2>&1; then "$selected_op" else echo >&2 "Error: Function '$selected_op' is not loaded or found." fi ``` **drun** ``` #!/usr/bin/env zsh # Interactive Docker Run (Ephemeral) _d_check_env fzf docker || return 1 local fzf_out key selection image_id cmd_run user_flag query query="$1" # Preview showing Image layers, Env vars and Labels local preview_cmd='docker inspect --format "Image: {{.RepoTags}} ---------------------------------------- ID: {{.Id}} Created: {{.Created}} Size: {{.Size}} Architecture: {{.Architecture}} ---------------------------------------- Labels: {{range \$k, \$v := .Config.Labels}}{{\$k}}={{\$v}} {{end}} " {2}' # Full-screen fzf to select image # Format: Repository:Tag | ID | Size | Created fzf_out=$(docker images --format "{{.Repository}}:{{.Tag}}\t{{.ID}}\t{{.Size}}\t{{.CreatedAt}}" | \ fzf --layout=reverse --border --query="$query" \ --header "Enter: Run --rm | Ctrl-R: Run as Root" \ --preview "$preview_cmd" --preview-window=right:50%:wrap \ --expect=ctrl-r) # Exit if user canceled [[ -z "$fzf_out" ]] && return 0 key=$(head -n 1 <<< "$fzf_out") selection=$(tail -n +2 <<< "$fzf_out") image_id=$(awk '{print $2}' <<< "$selection") [[ -z "$image_id" ]] && return 0 # Toggle root user user_flag="" [[ "$key" == "ctrl-r" ]] && user_flag="--user root" # Smart shell execution local smart_shell="sh -c 'exec \$(command -v zsh || command -v bash || echo sh)'" cmd_run=$(echo "docker run --rm -it ${user_flag} ${image_id} ${smart_shell}" | tr -s ' ') _d_print_cmd "$cmd_run" "\033[1;36m" eval "$cmd_run" ``` **dstop** ``` #!/usr/bin/env zsh # Batch Stop/Kill Docker Containers _d_check_env fzf docker || return 1 _d_has_running_containers || return 0 local fzf_out key selection action_cmd color # Multi-line preview command rearranged for better UX: # 1. Core container metadata (IP, Ports, Compose). # 2. Live Resource Usage placed HIGH up so it's always visible (preventing accidental stops of busy containers). # 3. Mounts placed at the BOTTOM (since devcontainers can have massive lists). # Note: You can scroll the preview window in fzf using Shift-Up/Shift-Down or mouse wheel. local preview_cmd='docker inspect --format "Container: {{.Name}} Status: {{.State.Status}} Compose Dir: {{index .Config.Labels \"com.docker.compose.project.working_dir\"}} IP Address: {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}} Ports: {{range \$p, \$conf := .NetworkSettings.Ports}}{{if \$conf}}{{\$p}} {{end}}{{end}}" {1} echo "------------------------------------------------------------" echo -e "\033[1;33m>>> Live Resource Usage (Snapshot):\033[0m" docker stats --no-stream --format "CPU: {{.CPUPerc}} | MEM: {{.MemUsage}} | NET: {{.NetIO}} | BLOCK I/O: {{.BlockIO}}" {1} echo "------------------------------------------------------------" echo -e "\033[1;34m>>> Mounts (Scroll down to see all):\033[0m" docker inspect --format "{{range .Mounts}}• {{.Source}} -> {{.Destination}} {{end}}" {1}' # Full-screen fzf with multi-selection enabled (-m) fzf_out=$(docker ps --format "{{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Status}}" | \ fzf -m --ansi --layout=reverse --border \ --header "TAB: Select | Ctrl-A: Select All | Enter: Stop | Ctrl-K: Kill (Scroll preview with Shift+Up/Down)" \ --bind "ctrl-a:select-all" \ --expect=ctrl-k \ --preview "$preview_cmd" --preview-window=right:55%:wrap) [[ -z "$fzf_out" ]] && return 0 # Extract the key pressed (Enter or ctrl-k) key=$(head -n 1 <<< "$fzf_out") # Extract only the container IDs (first column) from the selected rows selection=$(tail -n +2 <<< "$fzf_out" | awk '{print $1}') [[ -z "$selection" ]] && return 0 # Convert selection string into a proper Zsh array local -a target_ids=("${(@f)selection}") # Default action is graceful stop action_cmd="docker stop" color="\033[1;33m" # Yellow color for stop # Switch to forceful kill if triggered by Ctrl-K if [[ "$key" == "ctrl-k" ]]; then action_cmd="docker kill" color="\033[1;31m" # Red color for kill fi # Print and execute the command _d_print_cmd "${action_cmd} ${target_ids[*]}" "$color" eval "${action_cmd} ${target_ids[@]}" ``` --- # Au-delà du monolithe - Organiser son workflow ZSH comme un pro > Arrêtez d'alourdir la configuration de votre shell. Découvrez pourquoi déplacer vos fonctions ZSH dans des fichiers autonomes améliore les performances et la maintenabilité, avec un navigateur de projets universel. --- ![Modular ZSH Workflow](/img/v2/repo_with_fzf.webp) **TL;DR** Un `~/.zshrc` surchargé ralentit votre terminal et transforme le débogage en cauchemar. En utilisant le `fpath` de ZSH et en modularisant vos fonctions dans `~/.zsh/fns`, vous obtenez un démarrage instantané du shell et un code plus propre. Cet article explique le « pourquoi » et fournit une « Super Fonction » pour naviguer instantanément dans vos projets. On y est tous passés : votre `~/.zshrc` commence avec 10 lignes et finit en monstre de 1 500 lignes. Il contient tout et n'importe quoi : réglages de thème, scripts Docker complexes et alias divers que vous avez oublié avoir écrits. C'est l'**anti-pattern du shell monolithique** (si vous partez de zéro, commencez par [mon guide d'installation de ZSH](/blog/zsh-install)). Dans cet article, je vais vous montrer pourquoi déplacer votre logique dans des fichiers autonomes au sein de `~/.zsh/fns` et comment le mécanisme `autoload` de ZSH peut rendre votre terminal plus réactif que jamais. Et pour finir, je partage `repo`, une fonction qui va changer votre façon de naviguer dans votre workspace. **Quick Jump:** [Se téléporter dans un projet](#teleporting-to-a-project) | [Comment le mettre en place](#how-to-set-it-up) ## Se téléporter dans un projet {#teleporting-to-a-project} Une fois `repo` en place (voir plus bas), il suffit de taper un fragment du nom d'un projet pour y sauter directement — pas de `cd`, pas de chemin complet à taper : ```bash $ repo blg Switched to: ~/repositories/blog $ pwd ~/repositories/blog ``` Un seul projet correspondait à `blg` parmi tout ce qui se trouve sous `~/repositories`, donc `repo` a complètement sauté le sélecteur et a fait un `cd` direct. Si plusieurs projets avaient correspondu, la même commande aurait ouvert un sélecteur `fzf` pré-filtré avec ces résultats. ## Le problème du monolithe {#the-problem-with-the-monolith} Quand vous mettez une fonction directement dans votre `.zshrc`, ZSH doit parser et charger ce code en mémoire **à chaque fois** que vous ouvrez un nouvel onglet. Si vous avez des dizaines de fonctions utilitaires pour [Docker](/blog/zsh-docker-functions), Kubernetes ou [Git](/blog/git-branches-gst), ces millisecondes s'accumulent. En déplaçant vos fonctions dans un dossier dédié (comme `~/.zsh/fns`), vous gagnez trois avantages majeurs : 1. **Chargement paresseux :** avec `autoload`, ZSH ne lit le fichier que lorsque vous tapez réellement la commande. Votre shell démarre instantanément. 2. **Maintenabilité :** si une de vos fonctions casse, vous allez dans le dossier `fns` et vous la corrigez. Plus besoin de scroller dans un océan de code. 3. **Portabilité :** vous pouvez versionner votre dossier de fonctions séparément et le partager entre plusieurs machines sans embarquer toute votre config spécifique à l'OS. ## L'ingrédient secret : `fpath` et `autoload` {#the-secret-sauce-fpath-and-autoload} Au lieu de faire un `source` des fichiers, on utilise le `fpath`. C'est un tableau de répertoires dans lesquels ZSH cherche les définitions de fonctions. Quand vous appelez une fonction, ZSH regarde dans ces dossiers. S'il trouve un fichier correspondant au nom de la commande, il le charge à la volée. ## 💎 La Super Fonction : `repo` (navigateur de projets universel) {#-the-super-function-repo-universal-project-navigator} En tant que développeurs, on change de contexte en permanence. On passe d'un backend PHP à un projet d'API, à un frontend Vue.js, à une application Python... et entre plusieurs dossiers de documentation — toute la journée. Faire `cd ~/repositories/project-xyz` des centaines de fois par jour, c'est du temps perdu. La fonction `repo` utilise `fd` (ou `find`) et `fzf` pour vous permettre de chercher parmi tous vos repositories git et : 1. **Sauter** dans le dossier. 2. **Ouvrir** votre éditeur préféré (VS Code, Cursor, Neovim). ### Pourquoi c'est mieux dans un fichier autonome {#why-this-is-better-in-a-standalone-file} Cette fonction contient de la logique pour gérer la profondeur des répertoires et le lancement de l'éditeur. Dans votre `.zshrc`, c'est du bruit. Dans `~/.zsh/fns/repo`, c'est un outil dédié. > **Prérequis d'installation:** > > Cette fonction nécessite `fzf` (voir [Introduction to fzf](/blog/linux-fzf-introduction) si vous ne le connaissez pas encore) et `fd` (ou `find`). Pour une expérience optimale, installez `fd-find`. ## Comment le mettre en place {#how-to-set-it-up} ### 1. Créer la structure {#1-create-the-structure} Commencez par créer le dossier où vivront vos fonctions « autonomes » : ```bash mkdir -p ~/.zsh/fns ``` ### 2. Configurer votre `~/.zshrc` {#2-configure-your-zshrc} Ajoutez ces lignes à votre `.zshrc`. C'est le seul « boilerplate » dont vous aurez besoin. Il dit à ZSH : « Regarde dans ce dossier, et si tu y trouves des fichiers, considère-les comme des fonctions que je pourrais vouloir utiliser. » ```zsh # Anonymous utilities autoloading fpath=(~/.zsh/fns $fpath) # -U Suppress alias expansion for standard behavior # -z Load using zsh style # ::t Don't load the function yet, just index it; will be loaded at the first use. autoload -Uz ~/.zsh/fns/*(.:t) ``` ### 3. Créer le fichier de la fonction `repo` {#3-create-the-repo-function-file} Créez un fichier `~/.zsh/fns/repo` (sans extension !) et collez-y le code suivant. **Project: \~/.zsh/fns** > **Note:** > > Assurez-vous que le nom du fichier est exactement "repo". N'ajoutez pas .sh ni .zsh. **repo** ``` #!/bin/zsh # Description: Navigate to projects and optionally open in an editor. # Usage: repo [search_path] # Private helper for repo to check dependencies. _repo_check_deps() { local missing_dep=0 local os_type os_type=$(uname -s) # Check for fzf if ! command -v fzf >/dev/null 2>&1; then printf "\033[0;31m[Error]\033[0m 'fzf' is not installed.\n" missing_dep=1 fi # Check for fd or fdfind if ! command -v fd >/dev/null 2>&1 && ! command -v fdfind >/dev/null 2>&1; then printf "\033[0;31m[Error]\033[0m 'fd' is not installed.\n" missing_dep=1 fi if [[ "$missing_dep" -eq 1 ]]; then printf "\033[0;33mHow to install:\033[0m\n" if [[ "$os_type" == "Darwin" ]]; then printf " brew install fzf fd\n" elif [[ -f /etc/debian_version ]]; then printf " sudo apt update && sudo apt install fzf fd-find\n" elif [[ -f /etc/fedora-release ]]; then printf " sudo dnf install fzf fd-find\n" else printf " Please install 'fzf' and 'fd' using your package manager.\n" fi return 1 fi return 0 } # 1. Run the private dependency check # Since _repo_check_deps is also in fpath, we can call it directly. if ! _repo_check_deps; then return 1 fi # 2. Determine the correct fd binary local fd_bin if command -v fd >/dev/null 2>&1; then fd_bin="fd" else fd_bin="fdfind" fi # 3. Setup Editor: Use $EDITOR, or fallback to 'code' local visual_editor="${EDITOR:-code}" # 4. Search logic local search_path="$HOME/repositories" local query="$1" local selected_dir selected_dir=$("$fd_bin" '^\.git$' "$search_path" \ --hidden --no-ignore --type d --max-depth 3 | \ sed 's|/\.git/\?$||' | \ fzf --reverse --height 40% --border --header 'Navigate to Project' \ --query "$query" --select-1 --exit-0) # 5. Handling exit if [[ -z "$selected_dir" ]]; then return 0 fi # Clean up trailing /.git selected_dir="${selected_dir%/.git}" selected_dir="${selected_dir%/.git/}" cd "$selected_dir" || return 1 printf "Switched to: %s\n" "$selected_dir" # 6. Open in Editor # We check if the editor command actually exists on the machine if command -v "$visual_editor" >/dev/null 2>&1; then # [Y/n] is the standard way to show Y is the default printf "Open in %s? [Y/n] " "$visual_editor" read -k 1 REPLY printf "\n" # Check if REPLY is a newline (Enter) OR Y/y if [[ "$REPLY" == $'\n' || "$REPLY" =~ ^[Yy]$ ]]; then "$visual_editor" . fi else printf "\033[0;33mNote:\033[0m Editor '%s' not found. Set \$EDITOR in your .zshrc\n" "$visual_editor" fi ``` > **Note:** > > Pensez à adapter la variable `search_path` à l'endroit où vous stockez vos projets et, si vous n'utilisez pas VSCode, à mettre aussi à jour la variable locale `visual_editor`. ### 4. L'utiliser {#4-use-it} Ouvrez un nouveau terminal ou lancez `source ~/.zshrc` (puisque vous avez modifié `.zshrc`) et lancez simplement `repo` pour démarrer la fonction. Plutôt sympa, non ? > **Recharger:** > > Si vous avez déjà utilisé l'alias et que, pour une raison quelconque, vous avez modifié le code, la commande à lancer est : `unfunction repo && autoload -Uz repo && repo`. ## Autres démos {#more-demos} ### Recherche rapide (Fuzzy Find) {#fast-search-fuzzy-find} Vous pouvez accélérer votre navigation en passant directement un motif de recherche à la commande : `repo ` — exactement l'exemple `repo blg` montré en début d'article. * Si plusieurs projets correspondent à votre query, `fzf` s'ouvre pré-filtré avec ces résultats. * Si **un seul** projet correspond, le script saute complètement l'interface et fait immédiatement un `cd` dans le répertoire. > **Astuce de pro:** > > C'est parfait pour les projets que vous visitez souvent. Si votre motif est suffisamment unique, ça devient un raccourci ultra-rapide ! ## Conclusion {#conclusion} La différence entre un environnement de développeur « junior » et « senior » tient souvent à la **friction de l'outillage**. En déplaçant vos fonctions dans des fichiers autonomes, vous réduisez la friction liée à la maintenance de votre environnement. Avec `repo` installé dans votre nouvelle organisation modulaire, vous pouvez désormais vous téléporter dans votre workspace en quelques secondes. La prochaine fois que vous écrivez un snippet utile, ne le collez pas dans votre `.zshrc`. Donnez-lui son propre logement dans `~/.zsh/fns/` — voir [ZSH Functions - Customizing Your Shell for Docker Management](/blog/zsh-docker-functions) et [Showing the last 3 updated branches when you jump in a git repo](/blog/git-branches-gst) pour d'autres ensembles de fonctions et de hooks organisés de la même manière. ---