- Corriger du texte en ligne de commande peut sembler être une fonctionnalité de base, mais le traitement de la saisie varie selon les programmes, si bien que une même touche peut se comporter de manière totalement différente
- L’expérience de saisie dans le terminal se répartit globalement en quatre catégories : absence de système de saisie, GNU readline, bibliothèques alternatives comme
libedit, et systèmes de saisie propres
- Dans des outils simples comme
cat, nc ou dash, les touches fléchées peuvent s’afficher sous forme de ^[[D, mais les commandes fournies par le terminal comme Ctrl+W, Ctrl+U, Ctrl+C et Ctrl+Z restent utilisables
- Les outils basés sur readline, comme
bash, psql, irb ou python3, fournissent l’historique, les touches fléchées, la recherche avec Ctrl+R et les déplacements Ctrl+A/Ctrl+E ; on peut ajouter une expérience similaire avec rlwrap
- Identifier le système de saisie en cours permet de comprendre rapidement pourquoi les touches fléchées ou
Ctrl+R se comportent différemment, et de choisir plus facilement une solution de contournement comme consulter la documentation ou utiliser rlwrap si nécessaire
Une expérience de saisie qui change selon les programmes
- La manière de saisir et de modifier du texte dans un terminal varie beaucoup d’un programme à l’autre
- Les principaux types se distinguent ainsi
cat, nc, git commit --interactive, etc. ne prennent pas en charge les touches fléchées, ce qui peut faire apparaître des chaînes comme ^[[D^[[D^[[C^[[C^
irb, python3 sous Linux, etc. fournissent des fonctions d’édition de base comme l’historique et les touches fléchées via la bibliothèque readline
- Dans certains cas, comme
/usr/bin/python3 sur Mac, les touches fléchées fonctionnent, mais Ctrl+left ou la recherche arrière avec Ctrl+R ne sont pas pris en charge
fish, ipython3, micro, vim, etc. utilisent leur propre système de saisie
Mode 1 : état de base sans système de saisie
- Si un programme se contente de recevoir du texte via un appel comme
fgets() sans fournir de fonction d’édition distincte, les touches fléchées peuvent être affichées sous forme de séquences d’échappement plutôt que comme des commandes d’édition
- Par exemple, dans
dash, appuyer sur la flèche gauche affiche ^[[D dans le terminal comme suit
$ ls l-^[[D^[[D^[[D
- Les fonctions de contrôle fournies par défaut par le terminal restent toutefois disponibles
- Saisie de texte
- Retour arrière
Ctrl+W : supprimer le mot précédent
Ctrl+U : supprimer toute la ligne
Ctrl+C : interrompre le processus
Ctrl+Z : suspendre le processus
- Même dans cet environnement,
Ctrl+W permet de supprimer mot par mot sans appuyer plusieurs fois sur Retour arrière
- Les codes de contrôle pris en charge par le terminal peuvent être vérifiés avec
stty -a
Mode 2 : les outils qui utilisent readline
- readline est une bibliothèque GNU qui rend la saisie de texte plus confortable, utilisée par de nombreux outils interactifs
- Les raccourcis readline fréquemment utilisés sont les suivants
Ctrl+E ou End : aller à la fin de la ligne
Ctrl+A ou Home : aller au début de la ligne
Ctrl+left/right arrow : se déplacer d’un mot en avant ou en arrière
- Flèche haut : passer à la commande précédente
Ctrl+R : rechercher dans l’historique
Ctrl+W et Ctrl+U du mode de base peuvent aussi être utilisés, mais dans readline, Ctrl+U ne supprime pas toute la ligne : il supprime du curseur jusqu’au début de la ligne
- La recherche dans l’historique avec
Ctrl+R de bash est une fonction fournie par readline
psql, irb, python3, etc. sont aussi des exemples de programmes qui utilisent readline
- La liste complète des commandes d’édition de readline est disponible dans le manuel de readline
Ajouter rlwrap à un outil sans readline
- Même un programme qui ne prend pas en charge readline, comme
nc, peut être utilisé comme un programme compatible readline en l’exécutant avec rlwrap nc
- rlwrap peut améliorer fortement l’utilisabilité des outils qui n’ont pas readline
- Il est aussi possible de configurer une autocomplétion personnalisée, mais ce n’est pas quelque chose qui a été testé ici en pratique
Pourquoi certains outils n’utilisent pas readline
- Plusieurs raisons peuvent expliquer pourquoi certains outils n’utilisent pas readline
- Des programmes très simples comme
cat ou nc peuvent ne pas vouloir ajouter une dépendance relativement lourde
- readline n’est pas sous LGPL mais sous licence GPL, ce qui peut être incompatible avec la licence du programme
- Si seule une partie du programme est interactive et que l’entrée se limite presque à un seul caractère comme
y ou n, la prise en charge de readline peut être peu prioritaire
git possède des fonctions interactives comme git add -p, mais lorsqu’une saisie longue et significative est nécessaire, il lance généralement un éditeur de texte
idris2 n’utilise pas readline afin de minimiser les dépendances, et suggère d’utiliser rlwrap si de meilleures fonctions interactives sont nécessaires
Comment vérifier si readline est utilisé
- La méthode la plus simple consiste à appuyer sur
Ctrl+R
- Si l’invite suivante apparaît, il y a de fortes chances que readline soit utilisé
(reverse-i-search)`':
- D’autres bibliothèques peuvent employer la même terminologie, ce n’est donc pas un critère absolument certain, mais aucun autre système utilisant l’expression
reverse-i-search pour la recherche dans l’historique n’est connu
Les raccourcis readline et Emacs
- Les raccourcis
Ctrl+A pour aller au début de la ligne et Ctrl+E pour aller à la fin de la ligne peuvent être considérés comme venant d’Emacs
- Dans Emacs aussi,
Ctrl+A et Ctrl+E ont le même comportement que dans readline
- En revanche,
Ctrl+W et Ctrl+U n’ont pas le même comportement dans Emacs et dans le terminal
- L’histoire du projet readline est détaillée dans l’histoire du projet Readline
Mode 3 : bibliothèques de saisie alternatives comme libedit
/usr/bin/python3 sur Mac est proche d’un état intermédiaire où seules certaines fonctions de readline sont prises en charge
- Par exemple, appuyer sur
Ctrl+left arrow peut afficher ;5D au lieu de déplacer le curseur d’un mot
$ python3
>>> importt subprocess;5D
- Dans l’installation Python par défaut de Mac OS, le module Python
readline repose en réalité sur libedit
- On suppose que c’est parce que readline est sous licence GPL
- La commande suivante permet de vérifier si ce Python utilise libedit
$ python3 -c "import readline; print(readline.__doc__)"
Importing this module enables command line editing using libedit readline.
- Python installé sous Linux ou via Homebrew utilise généralement readline
- Python 3.13 devrait supprimer la dépendance à readline et utiliser sa propre bibliothèque ; à l’avenir, l’affirmation « Python utilise readline » pourrait donc ne plus être toujours vraie
Mode 4 : systèmes de saisie propres
- Certains programmes utilisent leur propre système d’édition de texte et offrent parfois davantage de fonctions que readline
- Exemples de programmes utilisant leur propre système de saisie
- La plupart des éditeurs de texte en terminal comme
nano, micro, vim ou emacs
- Des shells comme
fish
- L’éditeur de ligne de
zsh, zle
- Des REPL comme
ipython
- Des outils comme
atuin
- Ces systèmes peuvent proposer les fonctions suivantes
- Une autocomplétion mieux adaptée à l’outil
- Une meilleure gestion de l’historique, avec coloration syntaxique
- Davantage de raccourcis clavier
Systèmes de saisie propres inspirés de readline
- Atuin est un outil de recherche dans l’historique du shell ; son implémentation de la saisie est propre, mais inspirée de readline
- Atuin tire parti du fait que les utilisateurs sont habitués aux raccourcis clavier de readline, en faisant fonctionner ces raccourcis sans utiliser directement readline
- prompt_toolkit, utilisé par IPython, prend également en charge de nombreuses options, dont des raccourcis de style vi
- Par défaut, prompt_toolkit prend en charge des raccourcis de style readline
- C’est comparable au fait que beaucoup de programmes prennent en charge les raccourcis vim de base, comme
j pour descendre et k pour monter
- Par exemple, Fastmail prend en charge
j et k, même si la plupart de ses raccourcis n’ont pas grand-chose à voir avec vim
- Les systèmes de saisie propres inspirés de readline peuvent être subtilement incompatibles avec readline, mais cela peut ne pas poser de gros problème aux utilisateurs qui n’emploient que quelques raccourcis de base
- Ces systèmes propres peuvent généralement offrir une meilleure autocomplétion que l’usage de readline seul
Les shells qui prennent en charge le mode vi
bash, zsh et fish prennent tous en charge un mode vi pour la saisie de texte
- Dans un sondage non scientifique mené sur Mastodon, 12 % des personnes ont répondu utiliser le mode vi
- readline possède lui-même un mode vi, et la prise en charge du mode vi par
bash fonctionne par ce biais
- Beaucoup d’autres programmes utilisant readline peuvent aussi disposer d’un mode vi
Démarche pour diagnostiquer l’état de la saisie
- Savoir quel système de saisie est utilisé rend le comportement en ligne de commande plus prévisible
- Dans une invite de saisie, on peut raisonner dans l’ordre suivant
- Si les touches fléchées ne fonctionnent pas, il est probable qu’il n’y ait pas de système de saisie ;
Ctrl+W et Ctrl+U restent toutefois utilisables, et l’on peut ajouter rlwrap si nécessaire
- Si
Ctrl+R affiche reverse-i-search, il s’agit probablement de readline, et l’on peut utiliser les raccourcis readline familiers ainsi que les fonctions d’historique de base
- Si
Ctrl+R déclenche un autre comportement, il s’agit probablement d’une bibliothèque de saisie propre, qui peut se comporter globalement comme readline ; il suffit alors de consulter la documentation si nécessaire
- Il reste d’autres complexités liées à la saisie qui ne sont pas abordées ici
- Les problèmes liés à
ssh et tmux
- La variable d’environnement
TERM
- Les différences de prise en charge du copier-coller selon les terminaux, comme GNOME Terminal, iTerm ou xterm
- Unicode
- Divers autres problèmes liés à la saisie
1 commentaires
Avis sur Hacker News
Les articles de Julia sont toujours excellents. Un point manquant : dans un script shell, utiliser
sttypermet de modifier de nombreux comportements du terminal, notamment la façon dont les entrées sont traitées.Une expérience que j’avais faite autrefois avec
shetstty: https://gist.github.com/alganet/63f1dbc97b8fd35f7bb14ec30f79...Dans la plupart des terminaux compatibles VT100 (mintty, xterm, Terminal.app, vscode, etc.), on peut capturer et interpréter dans le shell la plupart des combinaisons de touches, ainsi que les gestes de souris (appui/glisser/relâchement).
La démo se lance avec
bash -c "$(curl -L https://git.io/fjToH)"; il suffit ensuite d’appuyer sur des touches ou de déplacer la souris. Pour quitter : Ctrl+W. zsh et ksh sont aussi pris en charge, mais pas dash ni d’autres shells dépourvus deread -rn1.De plus, en redirigeant un programme interactif vers
cat -v, comme avecvi | cat -v, on peut voir les échappements VT100 qu’il utilise ; j’ai beaucoup appris en observant comment vi procédait.stty sanepour le remettre d’aplomb.Ouvrez trois terminaux et lancez 1)
man ascii, 2)nc -lvp 9001 | xxd -c1, 3)stty raw -echo; nc -nv 127.0.0.1 9001, puis, dans le terminal 3, essayez de reproduire dans l’ordre toute la colonne « Hex » de la page de manuel.On peut observer que certaines valeurs peuvent être produites de plusieurs façons, et que certaines autres touches envoient une charge utile sur plusieurs octets.
sttyet le mode brut : https://viewsourcecode.org/snaptoken/kilo/, https://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode...Les paramètres et symboles de
sttyqui semblent magiques viennent de séquences de contrôle définies il y a longtemps. Des standards obscurs des années 1970 comme ECMA-48 contiennent beaucoup de terminologie ésotérique aujourd’hui presque inutilisée.Par exemple, Select Graphic Rendition(SGR) ressemble en gros à des balises HTML version années 1970 : pour définir la couleur de premier plan, on écrit quelque chose comme
ESC[38;2;R;G;BmTextESC[0m.ESC[est CSI,mest SGR,38désigne la couleur de premier plan,2l’espace de couleur RGB, et0la réinitialisation du style.Comme les paramètres précèdent la commande, la structure paraît un peu inversée. Comme les navigateurs, les terminaux ont toujours varié — et varient encore — dans l’étendue et la manière dont ils implémentent les fonctionnalités. Les couleurs sont assez bien prises en charge, mais pas le reste. Par exemple,
SGR<7>est censé être un mode “negative image”, mais le standard ne l’explique pas en détail : certains terminaux l’affichent en inverse vidéo, d’autres le traitent autrement, ce qui cause encore des problèmes aujourd’hui.Il y a plus de 20 ans, j’ai construit une machine à états au-dessus de readline pour en faire non pas simplement une ligne qui se replie, mais un véritable éditeur multiligne où l’on peut se déplacer vers le haut et vers le bas.
Vidéo : https://github.com/colmmacc/jot/raw/master/jot-demo.mp4
Le dépôt CVS du client IM Unix en terminal qui intégrait cette fonction : https://c-hey.redbrick.dcu.ie/src/c-hey_cvs_latest/
Quand on se déplace entre les lignes, il suit le curseur et redessine l’affichage ; si la taille du terminal change, il observe SIGWINCH et redessine. J’aimerais le réécrire en Rust et le publier sous forme de petite bibliothèque, mais je n’en ai pas eu le temps. J’ai toujours été surpris que personne n’ait fait quelque chose de similaire.
Après
autoload -Uz zed, on peut utiliserzedpour éditer un fichier temporaire et le passer àgit commit -F, ce qui lance un éditeur inline disposant de toutes les fonctionnalités de ZLE. Il suffit de sauvegarder avecC-x C-wpour valider le commit, ou d’annuler avecC-c.L’avantage de cette implémentation de base est que
zedest un nouveau widget ZLE, ce qui donne un contrôle complet sur le keymap. Le mode emacs ou le mode vi fonctionne immédiatement, et on peut personnaliser davantage.En écrivant directement un widget ZLE, on peut l’appeler depuis l’éditeur de ligne lui-même, sans avoir besoin d’envelopper la commande comme ci-dessus.
https://github.com/zsh-users/zsh/blob/master/Functions/Misc/...
git commit. L’effet « machine à écrire inline » est aussi très réussi.Parmi les éléments absents de l’article, il y a les caractères larges, le problème des différences de modes clavier qui font qu’une même frappe peut être représentée par des séquences d’échappement ANSI différentes, les différences d’état TTY comme l’écho local, et le fait que les appels système qui modifient l’état du TTY varient subtilement selon les systèmes d’exploitation.
La prise en charge de l’émulation de terminal varie aussi beaucoup. Aujourd’hui, la plupart imitent xterm, mais même là, personne ne vise vraiment une compatibilité à 100 %.
Il manque aussi un consensus sur la manière de déterminer les fonctionnalités fournies par un terminal. Certains envoient des séquences d’échappement ANSI et attendent une réponse, d’autres regardent
$TERM, et certains terminaux s’annoncent comme xterm tout en ignorant les séquences de fonctionnalités VT et en définissant d’autres variables d’environnement. Honnêtement, c’est encore plus le bazar que les chaînes User-Agent.Tout cela suppose déjà des systèmes POSIX ; si l’on ajoute Windows, cela devient deux fois plus intéressant.
Dans bash, si vous définissez
$EDITORsur votre éditeur préféré, Ctrl-X Ctrl-E permet d’envoyer la ligne courante à$EDITOR. Après avoir modifié la commande, l’enregistrer puis quitter l’éditeur l’exécute.v.fcfait la même chose. Il suffit de taperfc, sans séquence avec la touche Ctrl.On peut aussi ouvrir la commande précédente avec un éditeur précis plutôt qu’avec le
$EDITORpar défaut. Par exemple, siEDITOR=vi, pour modifier la commande précédente avec ed, on peut utiliserfc -e ed.Il existe aussi une façon rapide de fabriquer un script shell à partir de l’historique en mode vi. Si la commande voulue est la 15e de l’historique, on peut faire
fc 15, puisw1.sh,%d,wq, par exemple.EDITOR=nvimavec copilot.vim comme une sorte d’autocomplétion IA pour le shell. C’est plutôt bien.Vu ici : https://twitter.com/arjie/status/1575201117595926530?s=46&t=...
Du point de vue d’un débutant sous Linux, on peut se demander pourquoi un programme qui lit simplement du texte avec quelque chose comme
fgets()bénéficie gratuitement de fonctions comme Backspace, Ctrl+W ou Ctrl+U.La documentation de
fgets()dit qu’elle lit jusqu’à un saut de ligne ou EOF ; la question est donc de savoir si, par défaut, elle bloque jusqu’à ce que l’utilisateur saisisse un saut de ligne et si, avant cela, le terminal permet d’éditer le tampon de ligne avec Backspace ou Ctrl+W/Ctrl+U.Mais rien ne garantit qu’un programme lise forcément une ligne entière : on peut passer
countà 2 et lire caractère par caractère. Dans ce cas, lorsqu’un Backspace survient, il ne serait pas possible de « reprendre » un caractère déjà lu ; je me demande donc s’il y a une magie dans le tampon interne defgets(), ou si, dans ce cas, l’édition de ligne se casse.https://en.cppreference.com/w/c/io/fgets
Si cela donne l’impression d’être tamponné par lignes, c’est en réalité parce que le shell le gère, ce qui permet aux développeurs d’ajouter des fonctions de manipulation de tampon texte, comme la complétion par Tab ou l’ajout de sudo au début d’une ligne.
Avant d’appuyer sur Entrée, les caractères affichés à l’écran sont seulement dans la mémoire du terminal, et l’appel système
readde l’application reste bloqué sur le descripteur de fichier du terminal. Tant que la ligne n’est pas complète, le terminal n’écrit pas les données.À ce moment-là, l’édition de la ligne en cours est généralement fournie par le terminal lui-même. Le fait que les caractères s’affichent à l’écran est aussi une fonction du terminal, appelée écho ; il en va de même pour Backspace, Suppr, Ctrl+C pour SIGINT et Ctrl+D pour EOF.
Les éditeurs de ligne et les éditeurs de texte désactivent ces fonctions et placent le terminal en mode brut (raw mode). Tout ce qui est saisi est immédiatement transmis à l’application, qui le traite elle-même. Un cas courant est aussi celui des invites de mot de passe, qui désactivent uniquement l’écho pour masquer les caractères.
La fin de ligne Unix traditionnelle
\nest en fait elle aussi une fonction du terminal. Du point de vue du terminal,\nne fait descendre le curseur que d’une ligne ; pour revenir au début de la ligne, il faut aussi\r. La vraie fin de ligne est\r\n, et le terminal transforme invisiblement\nen\r\n. Cela aussi peut être désactivé.En mode raw, cela ne fonctionne pas ainsi.
helpde VMS vers SunOS. Le premier obstacle était de trouver comment réagir à chaque frappe sans attendre Enter/Return.Mon responsable a volontairement très peu aidé, et m’a laissé découvrir cela presque seul en 1986, à une époque où il n’y avait pas de Web et presque pas de livres O’Reilly. Je lui en serai éternellement reconnaissant.
Le shell dash prend aussi en charge les modes d’édition s’il est compilé avec libedit. Les dérivés de Debian ne le font probablement pas pour des raisons d’espace, mais je pense qu’il vaudrait mieux l’activer afin que les personnes ayant commencé avec bash aient moins de choses à réapprendre ensuite.
Le standard POSIX définit aussi
set -o vicomme une extension optionnelle. Si un shell qui revendique la compatibilité POSIX implémente ce mode d’édition, il doit obligatoirement prendre en chargeset -o vi.« [set -o] vi : permet l’édition de la ligne de commande du shell à l’aide de l’éditeur vi intégré. L’activation du mode vi doit désactiver tout autre mode d’édition de ligne de commande fourni comme extension par l’implémentation. »
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V...
Je continue de penser que le terminal est le facteur unique qui maintient Linux éternellement sous les 5 % de parts de marché
Le problème n’est pas seulement la saisie de texte : toute l’expérience est complexe. 95 % de la population veut juste faire voler un drone, et on leur met sous les yeux un cockpit de Concorde sans étiquettes
https://qph.cf2.quoracdn.net/main-qimg-2566f4c91b894e4169d77..., https://media.thedroningcompany.com/images/tincy/WQZpC56vqMp...
Il y a bien un cockpit de Concorde derrière Ubuntu, et je suis plutôt reconnaissant de pouvoir y accéder sur mon portable Ubuntu de travail. Je pense que je n’aimerais pas ne pas avoir ce niveau de contrôle
En revanche, le portable de ma mère tourne aussi sous Ubuntu et celui de ma petite amie sous Manjaro ; toutes deux les utilisent très bien en se contentant de « faire voler le drone », sans jamais mettre les pieds dans le cockpit de Concorde. Bien sûr, moi, j’utilise Arch
Le terminal est un outil pour développeurs et administrateurs système. C’est juste que la plupart des utilisateurs de GNU/Linux sur desktop sont des développeurs et des administrateurs système, ce qui donne cette impression ; ils ne représentent que quelques pour cent de la population
Là où il faut connaître beaucoup de raccourcis bizarres, c’est plutôt le terminal de macOS, où tout est rendu aussi peu intuitif que possible
C’est peut-être une opinion impopulaire, mais dans iTerm2, je suis passé au préréglage Natural text editing pour l’aligner sur les raccourcis d’édition macOS traditionnels
Comme c’est généralement remappé en séquences de contrôle dans l’application de terminal, l’avantage est qu’on obtient la fonctionnalité presque partout, sans devoir modifier plusieurs endroits ni se soucier de la prise en charge de readline
Ce n’est pas parfait : il m’est arrivé que des séquences à saisir manuellement soient remappées, mais comme cela correspond à ma mémoire musculaire, j’utilise beaucoup plus l’édition de commandes
https://pliszko.com/blog/post/2021-10-31-natural-text-editin...
Je ne veux pas que l’édition de texte dans le terminal se comporte de manière spéciale
Les trois raccourcis clavier readline de base que les gens devraient connaître pour se simplifier la vie sont Ctrl+W, Ctrl+O et Ctrl+R
Ctrl+W supprime le dernier mot, comme Julia le mentionne dans son article
Ctrl+O exécute la ligne rappelée depuis l’historique, puis rappelle la ligne suivante de l’historique. Si vous allez sur la première commande dans l’historique puis appuyez cinq fois sur Ctrl+O, vous pouvez exécuter cinq commandes dans l’ordre
Ctrl+R recherche en arrière dans l’historique pendant que vous saisissez une chaîne de recherche. Appuyer à nouveau sur Ctrl+R passe au résultat précédent, tandis que Ctrl+S avance dans le temps. On exécute avec Enter ou Ctrl+O
Pour utiliser cette combinaison de touches pour la recherche vers l’avant, il faut savoir pourquoi elle ne marche pas et être prêt à désactiver XOFF
Dans Windows Terminal, Ctrl+C se comporte toujours comme il faut. Si du texte est sélectionné, il le copie ; sinon, il tue le processus en cours d’exécution.
Les applications de terminal sous Linux ne copient pas avec Ctrl+C et exigent Ctrl+Shift+C. Pour coller, si l’on appuie sur Ctrl+V, des caractères étranges sont insérés, alors que toutes les autres applications utilisent Ctrl+V, ce qui fait qu’on se fait constamment piéger par la mémoire musculaire.
Je me demande s’il existe une application de terminal Linux qui se comporte comme Windows Terminal et ne vous tape pas sur les doigts pour « usage inapproprié du télétype ».
Cette méthode utilise un tampon Xorg distinct : ainsi, même après avoir copié quelque chose avec Ctrl+C puis sélectionné un autre texte, Ctrl+V collera le premier, tandis que le clic du milieu collera le second.
Sous Windows ou sur smartphone, il m’arrive d’être agacé parce que j’ai seulement sélectionné quelque chose et oublié d’appuyer sur Ctrl+C ou équivalent.
Je ne pense pas qu’il existe de terminal Linux qui fasse comme Windows Terminal. Un programme exécuté dans le terminal peut vouloir gérer Ctrl+C lui-même. Un éditeur de texte en est un exemple, et le terminal ne sait même pas quel programme est en cours d’exécution. Le contrôle des tâches relève du shell.
map ctrl+c copy_or_interrupt.https://sw.kovidgoyal.net/kitty/actions/#action-copy_or_inte...