Aller au contenu principal

Apperçu rapide

Bash propose des raccourcis bien utiles pour relancer des commandes, ou des bouts de commandes, précédemment exécutées. Cette page va recenser ceux que j’ai trouvés les plus utiles jusqu’à présent.

Quand le présent rejoint le passé, allégorie. Cliquer pour voir l’image en plus grand

Voici quelques exemples d’usage:

# Relance la dernière commande en la faisant précéder par sudo
sudo !!
# Opération inverse: relancer la dernière commande sudo sans sudo
!*
# Pour modifier le dernier fichier visualisé avec less
!less:s/less/vi/

Tableau récapitulatif

Et voici un tableau à garder sous la main en cas de besoin

!! dernière commande
!rm dernière commande qui commence par rm
!?force? dernière commande qui contient force
   
!$ dernier argument de la dernière commande executée
!^ premier argument de la dernière commande executée
!* tous les arguments de la dernière commande executée (synonymes: !!:1* !!:1-$ !!:*)
   
!scp$ dernier argument de la dernière commande qui commence par scp
!scp^ premier argument de la dernière commence qui commence par scp
   
:p affichage au lieu d’exécuter
:h dirname (head)
:t filename (tail)
:r enlève l’extension (peut être utilisé deux fois pour des fichiers tar.gz)
:e retourne l’extension seulement
   
^search^replace remplace search par replace dans la dernière commande
!rm:s/search/replace/ remplace search par replace dans la dernière commande commençant par rm
!rm:sg/search/replace/ remplace partout …
   
sudo !! relance la commande précédente avec sudo
chmod +x !$ un exemple parmi mille de l’utilisation du racourci !$
!scp^:h le chemin du fichier copié par scp (si le fichier était le premier argument à scp ie il n’y avait pas d’options)
   
!grunt:2-$:p affichage tous les arguments sauf le premier de la dernière commande commençant par grunt
   

Contexte

Ça y est. Après des mois de travail appliqué, le projet est achevé: toutes les fonctionalités ont été implementées, la couverture de tests est satisfaisante, les scripts de déploiement vers les différents environnements fonctionnent.

Bref le client est satisfait et l’heure est venue de transmettre le projet. Cet fois-ci le client souhaite être autonome et continuer seul à exploiter et faire évoluer le logiciel. C’est son choix, c’est son droit, pas de problème.

Une dernière vérification sur le dépôt dévoile une petit souci: le script de déploiement, lors de la phase de construction du paquet, utilise un fichier auth.json utilisé par composer afin d’accéder à des composants php sous licence. Et ce fichier est enregistré dans le dépôt. Donc si le projet est livré en l’état au client, celui-ci continuera à avoir accès aux dépôts privés.

Porte ouverte sur un espace délabré. Cliquer pour voir l’image en plus grand

Le problème

Après consultation du département des ventes, nous apprenons que nous devons distinguer plusieurs cas:

  • il y a des composants pour lesquels le client a acquis les droits sur les mises à jour futures;
  • pour d’autres logiciels, seule la version disponible au moment de la livraison devra rester accessible;

Pour les premiers composants, les identifiants peuvent rester dans le fichier auth.json sans problème. Lorsque les futurs développeurs du projet lanceront la commande composer update les dernières versions seront alors téléchargées. En revanche pour les autres composants (un seul en fait) il va falloir faire autrement.

La méthode va être la suivante:

  1. retirer les clefs d’accès au dépôt du composant en question du fichier auth.json
  2. insérer «/en dur/» dans le dépôt git les sources du composant, et mettre à jour les fichiers composer.json et composer.lock en conséquent.

Nous allons nous simplifier la tâche: la deuxième partie (copie du composant dans le dépôt) sera mise en œuvre dans un nouveau commit. C’est un peu de la triche parce que cela signifie que le projet livré ne tournera pas ou ne pourra pas être construit avec les commits précédents le dernier. Néanmoins:

  1. c’est contractuellement Ok;
  2. cela fera à peine un peu plus de travail pour les nouveaux développeurs lorsqu’ils voudront utiliser un git bisect.

Quant à la première partie du nettoyage de notre dépôt git, nous allons devoir utiliser un outil diabolique: git filter-branch.

La solution

Cet outil est tellement diabolique que la documentation officielle vous suggère d’utiliser une alternative plus sûre comme filter-repo. Après avoir attentivement lu la documentation des deux outils, j’ai préféré utiliser le plus dangereux des deux, filter-branch. Pas par amour du risque, mais par besoin de simplicité.

Les deux outils permettent de re-écrire l’historique d’un dépôt git. Modifier l’historique d’un dépôt git n’est pas anodin. Une fois le serveur de référence mis à jour avec le nouvel historique, tous les développeurs devront re-cloner leur dépôt avant de pouvoir pousser à nouveau leurs modifications. Bien sûre, dans le cas décrit ici, cela n’est pas problématique, puisqu’il n’y aura que des nouveaux développeurs.

Alors que filter-repo permet essentiellement de modifier l’historique d’un dépôt git en supprimant, ajoutant ou modifiant des fichiers dans chaque commit, filter-branch permet d’exécuter une commande avant chaque commit. Dans notre cas, nous allons écraser le fichier auth.json avec la version contenant les clefs permises.

git filter-branch --tree-filter 'cp /tmp/auth.json src/auth.json' HEAD

Et voilà!

À noter:

  1. tous vos commits ont maintenant des identifiants différents;
  2. le fichier src/auth.json a été automatiquement ajouté au premier commit, même si cela n’était pas le cas avant.

Mon projet comportait quelques centaines de commits. La commande n’a pas mis plus de quelques minutes pour s’exécuter sur un ordinateur type desktop.

Encore une fois, la commande git filter-branch est à utiliser avec parcimonie, et uniquement en dernier recours.

Astuces de printemps / aka nouvelle fournée de Petits Tips

D’après le calendrier c‘est le printemps. Il est temps de publier une nouvelle liste de petits trucs. Dorénavant j’appellerai ces articles «p'titstips».

Parterre de fleurs

Supprimer le canal alpha des images

À cause d’un bug dans une librairie utilisée dans un projet, toutes les images avec un fond transparent apparaissaient avec un fond noir. En attendant une version corrigée de la librairie, il a été décidé de mettre un fond blanc aux images avec transparence. Voici la commande qui réalise cette transformation:

convert -resize 200 -background white -alpha remove pic-with-apha.png pic-with-white-bg.png

Docker

Lorsque l’hébergeur de votre site vous a mis à disposition une base de donnée dans un container, mais que le container n’est pas démarré automatiquement lorsque le serveur redémarre, il faut changer la configuration du container comme ci-dessous.

docker update --restart always mysql-server-container

Redirection de port à l’intérieur d’un container

Imaginons une application web dans un container, dont le port externe au container 8092 est redirigé vers le port interne 80. Notre application est configurée pour être appelée sur l’adresse 127.0.0.1:8092. Mais lorsque l’application s’appelle elle-même depuis l’intérieur du container, le port 8092 n’est pas atteignable. Il faut donc créer une redirection en utilisant socat par exemple:

socat tcp-l:8092,fork,reuseaddr tcp:127.0.0.1:80

Convert mp4 into gif

Comme souvent une image veut mieux qu’un long discours, et en description de bug, une vidéo vaut mieux qu’une image. Vokoscreen est donc votre ami pour les déclarations de bug, cet outil vous permettant d’enregistrer ce que vous faites à l’écran. Et lorsque vous avez enregistré votre bug mais que le formulaire du service client pour ouvrir un ticket n’accepte que le format gif, alors ffmpeg vient à la rescousse.

ffmpeg \
  -i vokoscreenNG-2021-04-18_12-25-57.mp4 \
  -r 15 \
  -vf "split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \
  bug-live-test-credit-card.gif

Background dynamique

Vous n’arrivez à choisir votre fond d’écran? Ne choisissez pas, et changer le fond d’écran toutes les cinq minutes.

while true ; do  for f in `ls *|sort -R` ; do feh --bg-max $f ; sleep 300  ; done; done

(n’a pas été testé avec autre chose qu’Awesome)

Contexte

Je suis en train de migrer progressivement toutes les mes mini-machines de mon réseau local de Raspberry 2 et 3 vers le Raspberry 4. J’utilise ces machines comme:

  • Firewall
  • NAS
  • Pihole

Le passage de l’USB 2 des anciennes versions à l’USB 3 de la version du Raspberry 4, m’a poussé à chercher le bottelneck de mon infrastructure.

Usb ou ethernet? À la recherche du goulot d'étranglement. Cliquer pour voir l’image en plus grand sur le site de Wikimedia Commons, license CC BY-SA 3.0

Le tableau magique

D’une page wikipedia, j’ai extrait les valeurs suivantes:

Connection bit/s MB/s
Fast Ethernet (100BASE-X) 100 Mbit/s 12.5 MB/s
Gigabit Ethernet (1000BASE-X) 1 Gbit/s 125 MB/s
2.5 Gigabit Ethernet (2.5GBASE-T) 2.5 Gbit/s 312.5 MB/s
5 Gigabit Ethernet (5GBASE-T) 5 Gbit/s 625 MB/s
10 Gigabit Ethernet (10GBASE-X) 10 Gbit/s 1.25 GB/s
USB 2.0 480 Mbit/s 60 MB/s
USB 3.0 (aka USB 3.1 Gen 1) 5 Gbit/s 500 MB/s
USB 3.1 (aka USB 3.1 Gen 2) 10 Gbit/s 1.212 GB/s
USB 3.2 (aka USB 3.2 Gen 2×2) 20 Gbit/s 2.424 GB/s

Conclusion: Pour profiter réellement de l’USB 3 du NAS il va falloir investir dans un switch 10 Gigabits.

Avant

Je suis assez vieux pour me souvenir de l’époque où l’on devait guetter à la radio le passage de nos hits préférés pour pouvoir les enregistrer et les ré-ecouter plus tard à loisir.

Vieille cassette audio. Cliquer pour voir l’image en plus grand, license CC BY-SA 3.0

Maintenant

Autres temps, autre mœurs. On trouve maintenant sur YouTube1 des albums entiers, qui n’attendent que la ligne de commande pour être téléchargé grâce à Youtube-dl.

Voici donc la fameuse ligne de commande:

youtube-dl -i -x -o "%(artist)s - %(album)s - %(playlist_index)02d - %(track)s.%(ext)s" \
 url-de-la-playlist

Cela vous permet de récupérer toutes les pistes d’un album mis en ligne sous forme d‘une playlist. Les constituants des noms des chansons sont alors:

  1. Le nom de l’artiste
  2. Le nom de l’album
  3. L’index de la chanson (ce qui permet de jouer les chansons dans l’ordre)
  4. Le nom de la chanson

Parfois le nom de l’artiste n’est pas renseigné, il faut donc le remplacer:

for f in NA*  ;do  eval mv \"$f\" \"${f/NA/Nom de l’artiste}\" ; done

Pour d’autres morceaux, c’est le nom de l’album qui manque et qu’il faut remplacer:

for f in *NA*  ;do  eval mv \"$f\" \"${f/NA/Nom de l’album}\" ; done

Notes de bas de page:

1

Hélas, je n’ai pas trouvé l’équivallent sur des instances d’Individious

Objectif

Dans cet article je vais expliquer comment faire démarrer son Raspeberry Pi 4 directement depuis un disque dur externe USB. Et pour être totalement moderne, nous démarrerons une debian ARM64, au lieu de Raspberry Pi OS qui est devenu persona no grata.

Raspbery Pi 4 dans son armure d’acier, qui le protège du chaud. Cliquer pour voir l’image en plus grand

Première étape, activer l’option boot on usb

Il faut commencer par se procurer une distribution dédiée au Raspberry, comme par exemple celle que vous pouvez construire vous même avec le projet pi-gen. On ne va pas utiliser cette distribution définitivement, mais uniquement pour mettre à jour et configurer le Raspberry Pi 4. Il faudra donc utiliser une carte SD, sur laquelle vous aurez installer cette distribution, et démarrer une première fois dessus.

La mise à jour du programme de démarrage du stocké dans la ROM du Raspberry Pi est parfois nécessaire. En allumant le Raspberry sans carte SD, mais connecté à un écran, vous devriez lire la version de l’Eeprom. Si la date est inférieur au Sep 3 2020, une mise à jour est nécessaire.

Pour mettre à jour la ROM, il vous faut, après avoir démarré avec la distribution dédiée, installer le paquet rpi-eeprom, puis lancer rpi-eeprom-update -a.

Après la mise à jour le deuxième rôle de cette distribution est de configurer l’Eeprom pour autoriser le démarrage depuis un disque dur externe. Le programme qui autorise cette configuration est rpi-eeprom-config --edit . Il faut changer la directive BOOT_ORDER. Plusieurs valeurs sont possibles. Le Raspberry Pi essaie de démarrer depuis les différentes options données, de droite vers gauche par priorité décroissante.

J’ai entré 0x41 comme valeur pour la directive BOOT_ORDER. Cela signifie:

  1. Essaie de démarrer sur la carte SD (valeur 1 la plus à droite);
  2. Si cela ne marche pas, essaie de démarrer sur le disque dur externe (valeur 4).

Si j’avais configuré le BOOT_ORDER à 0x242, le Raspberry Pi aurait essayé de démarrer, dans l’ordre:

  1. depuis la carte SD;
  2. depuis un disque dur externe
  3. depuis le réseau (valeur 2).

Maintenant il ne nous reste plus qu’à installer une debian ARM64 sur un disque dur externe.

Installation de Debian ARM

Il existe un mini site web dédié aux images Raspberry PI construites par Debian. Les images de la distribution debian ARM64 les plus stables se trouvent ici.

La première chose à faire est de brancher un disque dur externe, de type USB3 de préférence, supportant l’uasp serait encore mieux. Ensuite on copie l’image sur le disque dur, exactement de la même manière que si c’était une carte SD.

Voici les instructions que je lance pour télécharger une image buster et la copier sur un disque externe. Ces instructions sont valables pour les images du 10 février 2021 (les plus récentes au moins de la rédaction de ce billets) et pour un disque dur apparaissant en /dev/sdb. Surtout n’exécuter pas ces instructions sans les comprendre et sans changer la valeur /dev/sdb, au risque d’écraser toutes les données d’un disque dur branché à l’ordinateur d’où vous les lancerez.

wget https://raspi.debian.net/verified/20210210_raspi_4_buster.img.xz
wget https://raspi.debian.net/verified/20210210_raspi_4_buster.xz.sha256
sha256sum --check 20210210_raspi_4_buster.xz.sha256
xzcat 20210210_raspi_4_buster.img.xz | sudo dd of=/dev/sdb bs=64k oflag=dsync status=progress

Ensuite, il n’y a plus qu’à retirer la carte SD du Raspberry Pi1, brancher le disque dur et redémarrer votre framboise.

En guise de conclusion

Et voilà, votre Raspberry Pi 4 contient une debian ARM64 pur jus, et il démarre sur un disque dur externe, généralement plus fiable que les carte SD.

À partir de là, à vous de continuer. Vous avez maintenant à disposition tous les paquets debian, il y a de quoi vous occuper :-)

Notes de bas de page:

1

Ce n’est pas nécessaire si vous avez configurer le démarage sur disque dur externe avant le démarrage sur carte SD

Le Besoin

Mon clavier sous x11 est configuré avec setxkbmap us intl. Cela me permet:

  1. d’utiliser un layout qwerty, avec lequel je suis le plus à l’aise;
  2. de taper tous les lettres accentuées françaises, y compris en majuscule (Éh oui!) et le ß allemand.

Sol couvert de feuilles d’automne

Malheureusement il n’était pas possible d’entrer:

  • le œ
  • le · ou pour l’écriture inclusive et faire enrager les reacs
  • pour envoyer de l’amour

La solution

Il faut activer la touche <Multi_key>. Dans mon cas, étant donné que:

  1. J’utilise AwesomeVM et j’ai donc besoin de la touche windows,
  2. j’utilise la touche Alt Gr pour écrire ß, entre autres choses,
  3. je ne crie jamais, et je n’ai donc presque jamais besoin de la touche capslock.

la solution consiste à utiliser la commande suivante: setxkbmap us intl -option compose:caps, qui convertit la touche capslock en touche [[https://fr.wikipedia.org/wiki/Touche_de_composition][compose]].

La liste des nouveaux caractères accessibles est disponible ainsi: cat /usr/share/X11/locale/en_US.UTF-8/Compose |grep ^\<Mul | less1

Voici un tableau avec quelques exemples des caractères que l’on peut taper avec cette nouvelles touches:

1 2 3 résultat
- >  
< -  
: )  
: (  
. -   ·
. =  
N o  
? !  
< 3  
( 123456789 - 20 ) ①②③④⑤⑥⑦⑧⑨ - ⑳
( aAbBcC - zZ ) ⓐⒷⓑⒷⓒⒸ - ⓩⓏ
- - . – en dash
- - - — em dash
# q  
# e  
# E  
# S  

Notes de bas de page:

1

En gros. Certains caractères ne sont accessibles qu’avec des claviers étrangers (grecque, ou asiatique par exemple).

Astuces de février

Pont en bois couvert de feuilles

Git

L’opération de rebase avec git modifie l’historique d’une branche. Si pour une raison ou une autre on souhaite annuler l’opération, plutôt que de rejouer chaque commit de la branche depuis l’ancienne origine de la branche, il est possible d’utiliser la commande git-reflog pour récupérer l’identifiant du commit

git reflog ; git reset --hard HEAD{@5}

Surveiller Mysql en mode débrouille

Voici une petite commande pour enregistrer tous les requêtes mysql qui durent plus de deux secondes, lorsque l’on n’a pas accès à des outils de monitoring plus évolué. En jouant sur l’intervalle de rafraîchissement de watch on peut surveiller des commandes plus longues.

watch -t "echo 'select now(); SHOW FULL PROCESSLIST;' | mysql --raw  >> ~/queries-log.out"

Notes: Cette commande récupère aussi les requêtes plus courtes, qui s’exécutent au moment où SHOW FULL PROCESSLIST est appelé.

Ajouter une image dans un document pdf

Par exemple pour ajouter une signature dans un document pdf. Les paramètres passés à geometry indique la taille et la position de l’image. La commande composite est par le paquet imagemagick.

composite -density 300x300  signature.png   -geometry 750x500+270+2950  document.pdf    document-avec-image.pdf

Changer de version php tous les paquets php

Petit enchaînement de pipes pour mettre à jour tous les paquets php.

apt-get install `dpkg -l | grep  php| awk '{print $2}' | sed 's/7\.3/7\.4/'`

Qu’est ce que la programmation procédurale

La programmation procédurale, aussi appelée génération procédurale, est la branche du développement logiciel dont l’objet est la création aléatoire d’un univers ou d’un objet virtuel. L’exemple typique, le plus connu, est celui du monde généré au début de chaque nouvelle partie Minecraft. Ce monde constitué d’îles, de plaines, de déserts, de collines, etc., bien que créé aléatoirement, possède une cohérence interne qui le rend réaliste, accessible: il n’y a pas de poissons dans les arbres, ni d’animaux de ferme dans les déserts chauds. Et ces derniers ne sont pas contigus à des zones froides.

Carte générée par l’algorithme diamant carré. Cliquer pour voir la carte en plus détaillée

Figure 1 : Carte générée par l’algorithme diamant carré

La génération procédurale peut aussi créer des villes, des cartes, des donjons, des planètes, des plantes, des êtres vivants ou encore des objets construits par l’homme, comme des maisons, des voitures, ou des vaisseaux spatiaux. Pour juger un algorithme de programmation procédurale on se posera la question suivante: «est-ce que cet objet aurait être conçu par un humain?»

Présentation du project Diamond Map

Depuis quelques années je suis fasciné par ce domaine de l’informatique. J’ai donc réaliser un générateur de carte de hauteur, basé l’algorithme diamant carré. Cet algorithme impose un certain nombre de contrainte sur la taille de la carte. Elle doit être carré, et la taille d’un côté doit être une puissance de deux plus un.

Mon implémentation suit à la lettre l’algorithme. La seule partie originale de mon travail a été le choix des couleurs pour représenter les différentes altitudes (et profondeurs): le blanc pour la neige des sommets et le noir pour les abysses des océans. Entre les deux, des roches, de la forêt, des plaines, du sable de plage, l’écume des vagues, et l’azur de la mer.

Le résultat est assez joli. Même si parfois, hasard oblige, la carte ne contient que la mer, ou au contraire, que le vague sommet marronnasse d’une montagne.

Nouvelle année

Chaise renversée avec des chaussures

Lancer plusieurs instructions make en parallèle

Après un `aptitude install parallel` voici comment exécuter en même temps deux cibles.

parallele make ::: clear-cache build-css

Grep avec des chaînes binaires

Très utile pour les `dumps` de tables contenant du texte binaire, résultat la fonction encode (cf https://mariadb.com/kb/en/encode/)

grep -a pattern sql-dump.sql > extract.txt

Composer

# because composer will never downgrade with `require`
composer require --no-update
composer update
composer why package-name
# error in json?
composer validate

Docker compose get an ip

Pour récupérer l’adresse IP du container `app` appartenant à un `docker-compose`.

docker inspect \
     --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' \
    `docker-compose ps -q app`

Bash

Lire le contenu d’un fichier csv, sans la ligne d’entêtes

cat toto.csv | tail -n +2

Awk

Utiliser awk pour générer un faux email dans un fichier csv incomplet (spécial dédicace aux clients qui fournissent des fichiers csv incomplets)

 awk -F ';' '{print $1,$2,$3,$4,$5,$6,$7,$8,$9,"fake-"$1"@null.io",$11,$12,$13}'  OFS=\;

Mysql

Générer des prix aléatoires dans une colonne:

update oxarticles set oxprice= round(round(RAND() * 10, 2)*10,2) ;