Ce weekend, nous avons découvert un ensemble d’outils magiques, qui m’étaient jusque-là inconnu ! Un ensemble qui, à première vue, ne paie pas de mine, mais qui peut offrir beaucoup de fonctionnalités sympathiques. Ils sont au nombre de trois : script, scriptlive, scriptreplay.

Contexte

Je vais bientôt sortir de mission et j’ai une bonne quantité de documentation à terminer. Je pense, notamment, à la documentation de certaines procédures que j’ai faites ou refaites un nombre incalculable de fois… la documentation de POC ou autre. Ne pourrais-je pas documenter ces procédures au gré de mes explorations ?

L’idée est la suivante : j’ai une opération de mise à jour manuelle de GitLab à réaliser, par exemple. Je souhaite pouvoir lancer une session d’un terminal, au sein duquel je vais enregistrer l’ensemble des commandes tapées, mais aussi leurs résultats. Si je peux ajouter de manière astucieuse un commentaire en passant, pourquoi pas. Je pars ensuite de cet ensemble pour générer un Markdown de ce type :

Nous listons les fichiers et documents dans le répertoire courant

ls -la

Voici la sortie de la commande :

total 44
drwxr-xr-x  2 dimitriq dimitriq  260 27 janv. 00:48 .
drwxrwxrwt 95 root     root     2480 27 janv. 03:27 ..
-rw-r--r--  1 dimitriq dimitriq  197 27 janv. 00:18 in
-rw-r--r--  1 dimitriq dimitriq 1951 27 janv. 00:05 parse_and_generate.go
-rw-r--r--  1 dimitriq dimitriq 1087 27 janv. 00:50 sheh.txt
-rw-r--r--  1 dimitriq dimitriq  154 27 janv. 00:04 template.md
-rw-r--r--  1 dimitriq dimitriq 1110 27 janv. 00:48 test.log
-rw-r--r--  1 dimitriq dimitriq  247 27 janv. 00:48 time.txt

Et ainsi de suite, vous avez l’idée ? Après, je repasse dessus pour ajouter les bons commentaires ou les bonnes explications.

Should I Script or Should I Script ?

Okay, go ! Que puis-je faire avec mon terminal pour rediriger stdin et stdout ?

  1. Utiliser > pour rediriger la sortie d’une commande vers un fichier. Par exemple, ls > fichier.txt enregistrera la sortie de la commande ls dans le fichier fichier.txt.

  2. Utiliser < pour lire l’entrée d’une commande à partir d’un fichier. Par exemple, cat < fichier.txt affichera le contenu du fichier fichier.txt.

  3. Utiliser >> pour ajouter la sortie d’une commande à un fichier existant. Par exemple, echo "nouvelle ligne" >> fichier.txt ajoutera la chaîne “nouvelle ligne” à la fin du fichier fichier.txt.

  4. Utiliser | pour rediriger la sortie d’une commande vers une autre commande. Par exemple, ls | grep txt affichera uniquement les fichiers dont le nom contient “txt”.

Stdout, c’est facile, stdin peut être une autre paire de manches.

Imaginons un script bash, qui démarre un bash, pour lequel je dois récupérer tout stdout et stdin.

Et là, je tombe sur cette pépite : script, ça date de 1970 quand même !!!!!

L’idée est assez simple, faisons quelques tests :

$ script test.log
Script started, output log file is 'test.log'.
$

Je reprends la main, j’ai un nouveau bash, et je peux taper les commandes que je veux :


$ echo "plop"
plop
$ ls -la
total 0
drwxr-xr-x  2 dimitriq dimitriq   60 27 janv. 04:35 .
drwxrwxrwt 96 root     root     2500 27 janv. 04:35 ..
-rw-r--r--  1 dimitriq dimitriq    0 27 janv. 04:35 test.log
$ exit

Résultat, un magni… attendez… what… bougez pas, je vous montre, vous allez halluciner !

Script started on 2024-01-27 04:35:13+01:00 [TERM="screen-256color" TTY="/dev/pts/2" COLUMNS="173" LINES="21"]
%

k/tmp/plop\]7;file://dimitriq/tmp/plop\
 dimitriq@dimitriq  /tmp/plop  [?1h=[?2004heecho "plop"echo "plop"[?1l>[?2004l

kecho\plop
%

k/tmp/plop\]7;file://dimitriq/tmp/plop\
 dimitriq@dimitriq  /tmp/plop  [?1h=[?2004hlls -lal-     l ls -lals -la[?1l>[?2004l

kls\total 0
drwxr-xr-x  2 dimitriq dimitriq   60 27 janv. 04:35 .
drwxrwxrwt 96 root     root     2500 27 janv. 04:35 ..
-rw-r--r--  1 dimitriq dimitriq    0 27 janv. 04:35 test.log
%

k/tmp/plop\]7;file://dimitriq/tmp/plop\
 dimitriq@dimitriq  /tmp/plop  [?1h=[?2004h[?2004l


Script done on 2024-01-27 04:35:49+01:00 [COMMAND_EXIT_CODE="0"]

What the hell, c’est quoi ce gibberish ?

Lors de l’utilisation de la commande script, les séquences d’échappement sont incluses. Ce sont des codes qui contrôlent l’affichage du terminal. Lorsque je déplace le curseur, que je change la couleur du texte dans mon terminal, des séquences d’échappement sont envoyées à ce dernier. Et c’est ces séquences qui rendent ce fichier illisible.

Ce fichier est appelé typescript, ce qui veut littéralement dire un script de tout ce qui est tapé, Javascript à encore frappé …

Utilisation de script

Enregistrement d'une session avec horodatage :

$ script -T timing.log my_session.log

Cela commencera l’enregistrement toutes les activités du terminal dans le fichier my_session.log, ainsi que les horodatages dans le fichier timing.log.

Lecture d'une session enregistrée (les commandes ne sont pas réellement exécutées) :

$ scriptreplay timing.log my_session.log

Cela rejouera la session enregistrée dans le terminal.

Exécuter à nouveau une session enregistrée (les commandes sont réellement exécutées) :

$ scriptlive timing.log my_session.log

Différents cas d’utilisations

  • Demo time : Imaginez une démo infaillible ? Non, mais juste imaginez ! J’ai déjà connu le très bon demo.sh que j’ai vu en conférence. Désormais, vous réalisez votre démo une fois et vous la présentez via scriptreplay.
  • Audit et log de toutes les sessions ouvertes sur un serveur.
  • Documentation.
  • Enseignement et formation : Enregistrez des sessions de terminal pour créer des tutoriels interactifs ou des guides étape par étape pour les étudiants ou les nouveaux employés.
  • Débogage et support technique : Enregistrez des sessions problématiques pour les partager avec des équipes de support ou des collègues afin de faciliter la résolution des problèmes.
  • Automatisation des tests : Utilisez les enregistrements pour tester des scripts dans différents environnements en simulant des interactions utilisateur.

Je suis ouvert à d’autres idées inspirantes sur l’utilisation de ces outils.

Conclusion

Bref, dans l’histoire, j’ai ce qu’il me faut pour mon projet. Avec quelques commandes sed et tr judicieusement utilisées :


sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" test.log | tr -dc '[[:print:]]\n' > new_output

Et hop, le tour est joué. J’ai un fichier beaucoup plus exploitable. Non, je plaisante, c’est une CLI en Go ou en Python qui fera le boulot derrière.

Je vous passe toutes les galères de regex, parce que des séquences d’échappement, si on y réfléchit bien, il y en a un certain nombre :

  • <CTRL>-<R> pour rechercher dans l’historique bash
  • <CTRL>-<W> pour supprimer le dernier mot avant le curseur
  • <TAB> pour la tabulation
  • <CR> pour le retour à la ligne (appui sur entrée)
  • etc.

En tout cas, pour le parsing, je m’arrêterai à 90 % et le reste fignoler à la main.

Ensuite, je souhaite approfondir mes connaissances sur les terminaux, donc attendez-vous à une suite.

Bonne nuit !