ATTENTION : Ce script est vraiment dangereux, ne l’utilisez pas si vous ne savez pas ce que vous faîtes. N’écrivez jamais de script contenant
rm
avec. Si vous écrivez une commande qui lit dans stdin il faudra le fermer en insérant un caractère de fin de fichier, en sortant du mode inserstion, en réinsérant un caractère de fin de fichier puis finalement en supprimant la commande qui pose souci. Vous êtes prévenu·es, c’est expérimental est tout cassé.
Objectif
L’objectif est d’avoir, l’un à côté de l’autre, un script et ses sorties STDERR
et STDOUT, en temps réel avec une mise à jour à chaque fois que l’on modifie le
script. Une interface comme celle-ci devrait permettre de faciliter l’écriture
de scripts dont la sortie nous intéresse, typiquement lorsqu’il faut traiter du
texte et quand itérer souvent et rapidement est souhaitable1. Elle pourrait
également être un bon outil pour l’enseignement de sed
, awk
, paste
, cut
,
tr
et toute la bande que j’aime bien.
Il n’est pas question ici d’analyser les actions du script et d’afficher en
français les impacts qu’il a eu sur le système (création/suppression de fichier
etc). Il est question de faciliter l’écriture d’une commande sed
un peu
dantesque sans avoir à systématiquement exécuter le script à la main.
L’idée me vient de fzrepl de Daniel F Gray. Je voulais quelque chose de similaire mais qui fonctionne pour une quantité arbitraire de commandes et éditable dans vim.
Au passage je découvre sans l’avoir prévu que ce système me permet de résoudre d’une manière bien plus puissante qu’auparavant un problème que j’avais documenté dans ce vieil article, section : “L’utilisation d’éditeurs de texte s’interfaçant bien avec le système permet de tester des bouts de markdown”. En effet, si j’ouvre l’un des fichiers sources de ce site avec la configuration vim qui va suivre j’obtiens automatiquement le résultat html dans la fenêtre droite.
Implémentation
J’ai pour le moment la flemme de gérer les arguments de façon à faire des
choses différentes selon si on passe un argument un script déjà existant ou
pas, selon si le chemin est absolu ou relatif etc. Les scripts tels que
présentés en dessous vont donc systématiquement tout créer en tant que fichier
temporaire. Si à la fin de votre expérimentation vous voulez sauvegarder le
script vous pouvez toujours lancer :w votre_script.sh
.
Avec vim tout seul
Commençons d’abord par créer les fichiers qui vont contenir le script et sa sortie et rendre le script exécutable :
script=$(mktemp)
data=$(mktemp)
chmod +x "$script"
Vient ensuite l’ouverture de vim et sa configuration :
vim -c "set autowrite"\
-c "autocmd TextChanged * silent! !$script > $data 2>&1"\
-c "autocmd TextChangedI * silent! !$script > $data 2>&1"\
-c "vnew $data"\
-c "set updatetime=100 | set autoread | autocommand CursorHoldI * checktime | autocommand CursorHold * checktime"\
-c "wincmd h | doautocmd TextChanged"\
"$script"
Vim est appelé avec tout un tas de commandes suivant l’option -c
qui
s’exécuteront automatiquement au lancement. Le denier argument est le chemin
vers le script.
Expliquons chacune des commandes vim :
set aw
: le fichier sera automatiquement écrit à chaque fois qu’une commande externe sera lancéeautocmd TextChanged * silent! !$script > $data 2>&1
: A chaque fois que le texte change en mode normal on exécute, en ignorant sa sortie et ses erreurs éventuelles (silent!
), le script dont on redirige tout vers le fichier de sortie- Idem mais en mode insertion
vnew $data
: on ouvre une fenêtre verticale avec le fichier de sortieset updatetime=100 | set autoread | autocommand CursorHoldI * checktime | autocommand CursorHold * checktime
: On modifie l’intervalle de mise à jour à 100ms, on met le buffer avec e fichier de sortie en lecture automatique (il se rafraichit à chaque changement), on force l’exécution dechecktime
à chaque première fois que le curseur ne bouge plus pendant 100ms après qu’il a bougéwincmd h | doautocmd TextChanged
: on revient sur la fenêtre avec le script et on déclenche une première fois l’un des évènements pour peupler le fichier de sortie (utile si l’on ouvre un script déjà existant)
Finalement on affiche les chemins des deux fichiers temporaire quand on ferme vim au cas-où l’on en est besoin :
echo "script : $script"
echo "données : $data"
Les avantages de cette version sont de ne pas dépendre de watch ni de tmux et de pouvoir parcourir la sortie dans un buffer vim avec tout ce que cela implique comme possibilité.
Avec vim + tmux + watch
Les dépendances sont :
- Une version relativement à jour de vim (qui a les évènements
TextChanged*
) - tmux
- watch
On commence de la même manière :
script=$(mktemp)
data=$(mktemp)
chmod +x "$script"
Puis on crée la session tmux :
tmux new \
vim -c "set aw"\
-c "autocmd TextChanged * silent! !$script > $data 2>&1"\
-c "autocmd TextChangedI * silent! !$script > $data 2>&1"\
-c "doautocmd TextChanged"\
"$script"\
\; split-window -h "watch -c -d -t -n 0.1 cat $data" \; select-pane -L
Finalement on affiche les chemins en sortie de session :
echo "script : $script"
echo "données : $data"
Les avantages sont de ne pas avoir à utiliser le hack vim un peu étrange à base
de CursorHold pour que tout soit à jour, de voir ce qui a été modifié en sortie
à l’aide de -d
de watch et être directement déposé dans tmux ce qui peut se
révéler utile pour retrouver votre travail plus tard. Évidemment ce dernier
point n’est pas essentiel si vous avez de toute façon l’habitude d’être dans
une session tmux quoi qu’il arrive.
L’espace entre les CLI des années 80 et les GUI
Au delà des raisons évoquées en intro j’ai une autre motivation à écrire ce genre d’outils.
De nombreuses critiques sont formulées envers les interfaces en ligne de commande. Même si je pense qu’elles sont parfois injustes et le fruit d’apriori culturels et techniques plus que de réelles limitations qui seraient par ailleurs inexistantes dans les autres formes d’interfaces, il convient d’admettre que l’expérience initiale dans un terminal linux est aride. Aurélien Tabard en résume bien les principales raisons :
Si la découvrabilité est en effet un point important, la gestion des erreurs l’est tout autant. Le undo n’est pas tellement développé dans les cli, pourtant la capacité d’essai-erreur-correction est facteur d’apprentissage important (oui il y a des façons de se tromper élégamment dans un terminal, mais le jour ou tu te trompes pour une raison ou une autre, le undo n’est pas là). D’autre notions permettent comme celles de gouffre d’évaluation et d’exécution peuvent expliquer le succès des gui. Enfin la capacité des systèmes à communiquer sur ce qu’ils permettent de faire (voir par exemple ce papier sur les affordances percues et le feedforward) est peu présente dans les cli.
De fait beaucoup de personnes sont introduites aux cli via des cours, des
tutos, des outils ne cherchant pas à faciliter l’apprentissage et l’usage des
cli et tombant la tête la première dans chacun des points soulevés par
Aurélien. Personnellement j’ai fait mes premières années de shell en école
d’ingénieur sans jamais savoir que l’on pouvait chercher dans l’historique des
commandes avec ctrl+r
, que beaucoup de commandes avaient des options pour
lister ce qu’elles allaient faire avant de les faire, que l’on pouvait mettre
beaucoup de couleurs dans les terminaux modernes, que l’existence des TUI
implique que l’on puisse faire des choses comme fzy, qu’il existait des shells
moins complexes que bash et avec une super auto-complétion pour un large
ensemble de commande ou une simple liste, même un peu indigeste, des commandes
généralement disponibles sur un Unix. J’ai le sentiment que la conséquence de
cela est que de nombreuses personnes, même dans le métier, n’ont qu’une vision
très limitée de ce que peuvent les cli et a fortiori des TUI qui fonctionnent
dans un terminal. Il est donc naturel que la plupart des personnes perçoivent
ces interfaces comme bloquées dans les années 70/80 et ne s’y tournent que pour
les usages à raison catalogués comme les plus appropriés. Et pourtant on peut
y mener la majorité de sa vie numérique.
Attention, contrairement à l’impression que j’ai trop souvent donné lorsque je parle de ce sujet, l’idée n’est pas de tout faire dans un terminal simplement parce que je kiffe le terminal. C’est parce que l’usage de GUI modernes me pousse à changer mon matériel, à mettre de la distance entre les personnes qui produisent les logiciels que j’utilise et moi et à cloisonner mes logiciels plutôt que de les faire causer entre eux que je m’intéresse à autre chose. Ne sachant pas où me tourner pour trouver une communauté et une culture ingénieurale axées sur le développement de GUI sobres, interopérables et techniquement simples, je me tourne vers une communauté et une culture qui, la plupart du temps, regroupe toutes ces qualités, j’ai nommé celle des personnes qui vivent dans un terminal Unix.
Je cherche donc, avec des outils comme celui décrit dans cet article, à montrer qu’il existe un espace entre les GUI d’aujourd’hui et les CLI des années 80 dans lequel on peut aborder les points soulevés par Aurélien tout en profitant des propriétés techniques plus avantageuses pour la sobriété de nos usages du numérique.