Aide-mémoire Bash
J'ai beau utiliser le shell Bash tous les jours depuis des années, j'en découvre toujours, régulièrement.
-
Lister les fichiers qui contiennent des chiffres entre foo et bar et uniquement des chiffres (une "wildcard" avec seulement des chiffres) :
ls foo+([0-9])bar
Il faut que l'option « extended globbing » soit active donc il faut d'abord faireshopt -s extglob
-
Effacer les fichiers qui commencent par bidule mais ne finissent pas par -small.jpg :
rm bidule!(*-small.jpg)
(ils peuvent avoir quelque chose entre bidule et -small.jpg, par exemple bidule_123456-small.jpg)
(même remarque pour l'option extglob) -
Passer une wildcard en argument d'un script bash : utiliser
$@
Par exemple, si j'appellemon_script.sh foo* bar*
dans mon_script.sh, j'ai$@
qui contientfoo123 foo456 bar1 bar2 bar3
-
Dans un script, afficher les commandes avant qu'elles s'exécutent, pour avoir du debug :
set -x
pour activer
set +x
pour désactiver
(on peut très bien l'activer pour une seule commande ou plusieurs, désactiver, réactiver plus bas) -
Inverser deux arguments de la ligne de commande :
Alt-t
(c'est Readline en fait, mais je le note ici) -
Déplacer un ou des arguments :
Ctrl-w
pour couper (autant que nécessaire) puisCtrl-y
pour coller
(idem, c'est Readline) -
Modifier la ligne de commande courante dans Vim :
Ctrl-x Ctrl-e
-
Plus généralement, modifier la ligne de commande précédente :
fc
-
Écrire sur la sortie d'erreur (stderr) :
echo truc >&2
(hé oui, même un truc aussi classique, parfois j'oublie) -
Inverser la sortie d'erreur et la sortie standard (stdout) :
commande_bidule 3>&1 1>&2 2>&3
Cette syntaxe se lit à l'envers pour chaque bloc, donc "copie 1 dans 3, 2 dans 1, et 3 dans 2", c'est le bon vieux "swapping" (échange) de variable ou descripteur de fichier. C'est utile quand on veut envoyer le résultat d'une commande ou d'un script dans un fichier et afficher la sortie d'erreur dans le terminal pour suivre son fonctionnement par exemple. -
Un raccourci pour
cp /foo/bar/baz /foo/bar/baz.BAK
:cp /foo/bar/baz{,.BAK}
-
Instruction "no op" :
: Coucou ; echo "foobar"
. Utile par exemple dans une ligne de crontab, pour avoir "Coucou" dans le sujet du mail sans avoir à faireecho "foobar" | mail -s "Coucou" untel
. -
Séparer une variable en plusieurs avec l'Instruction "here string" :
read chose truc bidule <<< "ceci est le contenu de ma variable"
$chose va contenir "ceci", $truc va contenir "est" et $bidule va contenir le reste -
Lire une liste contenue dans un fichier, par exemple une liste à deux colonnes "date nom" :
while read date nom ; do echo "$nom --> $date" ; done < fichier
-
Idem pour une liste à deux colonnes séparées par point-virgule "date;nom" :
IFS=";" ; while read date nom ; do echo "$nom --> $date" ; done < fichier
(ne pas oublier de remettre IFS à sa valeur initiale parunset IFS
ensuite sinon risque de surprises) -
Une boucle for sur la sortie de ls avec des fichiers dont les noms comportent des espaces :
IFS="
(ici ls trié par date de fichier, et [] pour visualiser)
" ; for f in $( ls -t ) ; do echo [ $f ]; done
(même remarque concernant IFS après) -
Un piège : à la fin de
bidule | while read line ; do foo=$line ; done
la variable $foo est vide ! Pourquoi ? Parce que le "tube" (pipe) provoque l'exécution d'un sous-shell. Pour éviter ça, il faut utiliser la "substitution de processus" avec la syntaxewhile read line ; do foo=$line ; done < <(bidule)
-
Comparer des chiffres décimaux :
if [ 12.3 -lt 4.56 ]; then
affiche l'erreur "nombre entier attendu"
Il faut utiliser une évaluation arithmétique du résultat de l'opération avec bc :if (( $( bc <<< "12.3 > 4.56" ) )); then …
La "here-string" <<< permet d'alléger l'écriture en évitant un echo … | bc
Les variables de type tableau (array) dans Bash
C'est la syntaxe la plus horrible que je connaisse mais il faut faire avec… Voici l'essentiel :
Tableau simple
- Déclarer :
truc=( bidule1 bidule2 bidule3 )
- Ajouter un élément à ce tableau :
truc+=( bidule4 )
- Nombre d'éléments :
echo ${#truc[@]}
- Afficher les valeurs :
for bidule in ${truc[@]} ; do echo $bidule ; done
- Afficher les indices et accès par index :
for i in ${!truc[@]}; do echo "$i => ${truc[i]}" ; done
- Effacer un élément :
unset truc[1]
Tableau associatif
- Déclarer :
declare -A machin=( [foo]=123 [bar]=456 [bidule]=truc )
- Ajouter un élément à ce tableau :
machin[chose]=coucou
- Nombre d'éléments idem :
echo ${#machin[@]}
- Afficher les valeurs idem :
for bidule in ${machin[@]} ; do echo $bidule ; done
- Afficher les clés et accès par clé :
for k in ${!machin[@]}; do echo "$k => ${machin[$k]}" ; done
- Effacer un élément :
unset machin[chose]
- Tester si un élément existe :
if [ ${machin[chose]+existe} ]; then echo "oui, il existe" ; fi
Ce "existe" après le caractère + c'est juste une chaine de caractère, on peut mettre n'importe quoi d'autre