Le site arthur.bebou.netlib.re - retour accueil
git clone git://bebou.netlib.re/arthur.bebou
Log | Files | Refs |
index.sh (10660B)
1 #! page 2 title: Un commentaire à propos de \"Avoid Software\" 3 author: Arthur Pons 4 description: Avoid software ? Embrace shell scripts 5 publication: 2025-05-15 6 7 section: main 8 9 ## Introduction 10 11 [Timothée](https://timothee.goguely.com/) a récemment fait un cours avec des 12 airs de [missing semester](https://missing.csail.mit.edu/) pour parler de JS, 13 de CLI et de vie professionnelle en tant que graphiste[^1]. A cette occasion là 14 il a trouvé une ressource parlant de CLI, faite par et pour des graphistes, 15 nommée [Avoid Software](https://avoidsoftware.sarahgarcin.com/index.html). 16 17 Cet article a pour but d'être une contribution à ce fanzine : 18 19 * en apportant des précisions sur le fonctionnement des scripts 20 * en proposant des alternatives aux scripts lorsqu' 21 * ils ne sont pas POSIX 22 * ils ne fonctionnent pas sur les fichiers contenant des espaces 23 * garantissant que tout fonctionne sous OpenBSD 24 * en proposant une partie expliquant comment transformer les commandes en 25 scripts qui peuvent prendre des arguments 26 27 ## Les dépendances 28 29 ## Les scripts 30 31 J'aurais tendance à dire qu'étant donné la manière dont ils sont présentés, ce 32 qui constitue cette partie sont plutôt des *commandes* que des *scripts*. 33 Habituellement les *scripts* vont être une ou plusieurs commandes inscrites dans 34 un fichier que l'on éxecute comme une seule commande par la suite. C'est du 35 détail mais si une section au sujet de la "scriptisation" des commandes devait 36 être ajoutée ça rendrait les choses plus claires. 37 38 ### Convertir des formats d'image 39 40 La commande `mogrify -format formatfinal *.formatàconvertir` a une 41 particularité qui n'est pas renseignée. Elle va convertir tous les fichier avec 42 l'extension `.formatàconvertir` dans le dossier courant. Ce comportement repose 43 sur l'utilisation des "globs" ou "shell patterns". Le caractère `*` veut dire 44 "n'importe quel caractère autant de fois que nécessaire". Donc 45 `*.formatàconvertir` veut dire "tous les fichiers se terminant par 46 `.formatàconvertir`. On peut penser les globs comme des expressions régulières 47 beaucoup moins puissantes. 48 49 D'autres caractères intéressants : 50 51 * `?` : "n'importe quel caractère une fois" 52 * `[]` : introduit ce que l'on appelle une "classe de caractère". En écrivant 53 `[abc]` on dit "une fois le caractère a, b ou c". Il est également possible 54 d'utiliser le caractère `-` pour décrire une étendue de caractère. `[3-8]` 55 ou `[d-h]` voudront dire "une fois un entier entre 3 et 8 inclus" et "une 56 fois une lettre minuscule entre d et h dans l'ordre alphabétique inclus". 57 * `!` : dans une classe de caractère permet de prendre le complément des 58 caractères. `[!abc]` veut dire "une fois n'importe quel caractère *sauf* a, 59 b ou c". 60 61 On peut donc étendre la commande présentée pour lui faire des choses plus 62 précises et alambiquées comme : 63 64 mogrify -format formatfinal *-[!1][0-9][0-9]-??.formatàconvertir 65 66 convertir tous les fichiers commençant n'importe comment, suivi d'un tiret, 67 suivi de n'importe quel caractère n'étant pas un `1` suivi de deux entiers suivi 68 d'un tiret suivi d'exactement deux caractères suivi de `.formatàconvertir`. 69 70 En arrière plan le shell "développe" le glob en le remplaçant par tous les 71 chemins auxquels il correspond. Si l'on a trois fichiers dans notre dossier 72 `a.jpg`, `b.jpg` et `machin.jpg` la commande 73 74 ls ?.jpg 75 76 va s'étendre en 77 78 ls a.jpg b.jpg 79 80 avant de s'éxecuter. Il est possible dans certains shell à l'écriture de la 81 commande d'obtenir un retour des fichiers qui correspondent en appuyant sur la 82 touche tabulation comme si l'on voulait compléter le glob. Avec ma configuration 83 de `zsh` le développement se fait carrément en direct dans le prompt. 84 85 Si vous êtes particulièrement alerte ce fonctionnement par réécriture de 86 commande devrait vous donner envie de tester un truc. Que se passe-t-il si un 87 le nom d'un fichier commence par `-` ? Admettons qu'il existe un fichier nommé 88 `-azerty.jpg` et que l'on utilise le glob `*.jpg` en argument de la fonction 89 `ls` : 90 91 $ ls *.jpg 92 # devient 93 $ ls -azerty.jpg 94 ls : option invalide -- 'z' 95 96 On se retrouve avec une erreur. Et pour cause, la commande a cru que 97 `-azerty.jpg` est la déclaration d'options de la commande `ls`. C'est d'autant 98 plus facile de tomber sur cette erreur que le tiret `-` arrive avant la plupart 99 des autres caractères dans l'ordre alphanumérique. 100 101 Il y a deux manières de se prémunir de cette erreur. La manière la plus 102 universelle est d'ajouter `./` devant le glob. `./` voulant dire "le dossier 103 courant" le glob se développera sur exactement les mêmes fichier qu'auparavant 104 mais les chemins démarreront tous `./` : 105 106 $ ls ./-azerty.jpg 107 -azerty.jpg 108 109 Il n'y a donc plus d'ambiguité entre les options et les noms des fichiers. Une 110 deuxième solution est d'ajouter `--` entre les options et les arguments de la 111 commande : 112 113 $ ls -larth -- *.jpg 114 # devient 115 $ ls -larth -- -azerty.jpg 116 -rw-r--r-- 1 arthur arthur 0 10 mai 11:27 -azerty.jpg 117 118 Ici `--` permet à la commande de savoir que tout ce qu'il suit doit être 119 interpréter comme des arguments et non pas comme de potentielles options. Bien 120 que [cette syntaxe soit 121 POSIX](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html#tag_12_02) 122 toutes les commandes ne respectent pas ce principe, en particulier les commandes 123 qui ne sont pas "de base" sur les systèmes Unix. La première solution est donc 124 préférable. 125 126 Des alternatives plus robustes de notre commande sont donc dans l'ordre de 127 préférence : 128 129 mogrify -format formatfinal ./*.formatàconvertir 130 mogrify -format formatfinal -- *.formatàconvertir 131 132 Bien sûr l'utilisation de ce genre de commande est habituellement personnelle et 133 faite dans un environnement maitrisé. Si l'on sait exactement comment sont 134 nommés nos fichiers il n'y a pas besoin de rendre plus robuste ces commandes. Je 135 propose ces alternatives au cas-où vous vouliez les partager avec des personnes 136 dont vous ne connaissez pas le nommage des fichiers et pour apprendre des choses 137 chouettes à propos du shell :) 138 139 ### Diffusion d'erreur 140 141 #### Modifier les images par lot 142 143 La commande 144 145 for img in *.jpg; do 146 convert "$img" -colorspace Gray -dither FloydSteinberg -colors 4 "dither_$img"; 147 done 148 149 contient la même faiblesse que la précédente. On peut la corriger simplement en 150 ajoutant `./` avant le caractère `*`. 151 152 Cette commande peut poser un autre problème. Le shell, lorsqu'il développe le 153 glob mais ne trouve aucun fichier correspondant, va tout de même tenter de 154 lancer la commande avec le glob littéralement : 155 156 $ ls *.truc 157 ls: impossible d'accéder à '*.truc': Aucun fichier ou dossier de ce type 158 $ touch machin.truc 159 $ ls *.truc 160 machin.truc 161 162 Donc dans notre commande s'il n'existe aucun fichier se terminant par `.jpg` 163 dans le dossier courant la commande `convert` va tout de même s'éxecuter une 164 fois avec `*.jpg` comme comme argument : 165 166 convert "*.jpg" -colorspace Gray -dither FloydSteinberg -colors 4 "dither_*.jpg"; 167 168 Ce qui n'est pas du tout ce que l'on voulait ! Dans le meilleur des cas cela 169 provoque une erreur un peu étrange à laquelle on s'attendait pas et dans le pire 170 des cas la commande fait sens pour le shell et opère sur des fichiers que l'on 171 ne voulait pas toucher. Malheureusement la solution à cela est un peu sale et 172 consiste à revérifier à l'intérieur de la boucle si `$img` contient le chemin 173 d'un fichier qui existe vraiment : 174 175 if [ -e "$img" ];then 176 cmd 177 fi 178 179 Une version plus robuste et portable serait donc : 180 181 for img in ./*.jpg; do 182 if [ -e "$img" ];then 183 convert "$img" -colorspace Gray -dither FloydSteinberg -colors 4 "dither_$img"; 184 fi 185 done 186 187 Dans certains shells il est possible de modifier ce comportement. Par exemple 188 dans bash et zsh : 189 190 shopt -s nullglob # Pour BASH 191 setopt NULL_GLOB # Pour ZSH 192 for img in ./*.jpg; do 193 convert "$img" -colorspace Gray -dither FloydSteinberg -colors 4 "dither_$img"; 194 done 195 196 Revient au même mais est plus efficace puisque l'on a plus à vérifier 197 l'existence du fichier à chaque itération de la boucle. 198 199 ### Redimensionner les images pour le web 200 201 Même remarques que pour la commande précédente. 202 203 ### Seam Carving 204 205 Maintenant que l'on sait ce que sont les globs on comprend pourquoi il faut 206 "échapper" le caractère `!` avec un `\` : 207 208 convert image.jpg -liquid-rescale 60x100%\! image_reduce.png 209 210 En l'occurence ce n'est pas un souci pour cette commande mais c'est une bonne 211 idée de le faire de manière générale sans quoi `!` pourrait être interprété 212 comme un glob et non pas comme un spécificité d'image-magick. 213 214 Je ne sais pas si c'est voulu mais cette commande converti un jpeg en png. 215 Peut-être est-ce une faute de frappe ? 216 217 ### Convertir des fichiers .mov en .mp4 218 219 RAS 220 221 ### Convertir des fichiers m4a en mp3 222 223 Même remarques au sujet du glob dans la boucle for : 224 225 for f in ./*.m4a 226 #plutôt que 227 for f in *.m4a 228 229 le renommage se fait avec la syntaxe `"${f%.m4a}.mp3"`. En shell il est possible 230 d'invoquer la valeur d'une variable `f` en écrivant `$f` ou `${f}`. La syntaxe 231 avec les `{}` permet deux choses : 232 233 * coller la valeur de la variable à une chaîne de caractère. En faisant `echo 234 "$ftruc"` le shell n'aurait pas la capacité de savoir que l'on veut ce qu'il 235 y a dans la variable `f` suivi de `truc`. Il penserait que l'on veut la 236 variable `ftruc`. La solution est d'écrire `echo "${f}truc"`. 237 * le retrait de suffixe et préfixe. `${f%.m4a}` renvoie la valeur de la 238 variable `f` avec le plus petit suffixe `.m4a` retiré. Autrement dit c'est 239 un moyen de retirer l'extension `.m4a` d'un nom de fichier. Il existe des 240 syntaxes similaires pour retirer le plus grand suffixe possible et les 241 préfixes les plus petits et grands. 242 243 `${f%.m4a}.mp3` est donc un moyen de prendre une variable `$f` dans laquelle on a 244 par exemple `truc.m4a`, lui retirer sno extension pour avoir `truc` et y ajouter 245 une nouvelle extension `.mp3` pour obtenir `truc.mp3` en fin de course. 246 247 ### Supprimer les espaces dans les noms de fichier 248 249 La commande `for f in *\ *; do mv "$f" "${f// /_}";` comporte plusieurs erreurs 250 ou faiblesses. 251 252 Premièrement il manque un `done` à la fin sans quoi on reste dans la boucle 253 `for` et le nous demande une commande supplémentaire. 254 255 Deuxièmement la désormais habituelle remarque au sujet de la protection contre 256 les fichiers commençant pas `-`. 257 258 ## Au sujet du nom 259 260 [^1]: https://mastodon.design/@timotheegoguely/114478679053770184