Bash
Raccourcis clavier
ctrl-a aller au début de la ligne
ctrl-e aller à la fin de la ligne
ctrl-d quitter la session (équivaut à "exit" ou "logout")
ctrl-l effacer tout affichage sur le terminal
ctrl-r<cmd> les occurrences de <cmd> dans l'historique
ctrl-s masquer la saisie (ctrl-q pour rétablir)
ctrl-t intervertir deux caractères
ctrl-u coupe tout ce qui précède le curseur
ctrl-v + <tab> insère une "vraie" tabulation
ctrl-w couper le mot précédent le curseur
ctrl-y coller ce qui a été coupé
alt-c effacer le mot précédent
alt-f aller au mot suivant
alt-b aller au mot précédent
alt-t intervertir deux mots (devant et derrière le curseur)
esc-. insère le dernier argument de la commande précédente (fonctionne également avec alt-.)
~~ ré-exécute la dernière commande
!XX ré-exécute la dernière commande commençant par XX
!XX:p affiche la dernière commande commençant par XX mais ne l'exécute pas
<cmd> !* exécute la commande <cmd> avec tous les paramètres de la dernière commande
<cmd> !$ exécute la commande <cmd> avec le dernier paramètre de la dernière commande
Astuces
Changer la forme et la couleur du prompt (invite de commande)
La forme du prompt shell est stockée dans une variable d’environnement : $PS1
.
bash-3.2$ export PS1="\u@\h:\w% "
marc@magma:~%
Les différents switch sont énumérés dans le manuel de Bash (section PROMPTING) ; ici le prompt affiche l’utilisateur, le nom de la machine ainsi que le répertoire courant. Il est possible de coloriser le prompt avec la syntaxe suivante :
export PS1="\033[01;37m$PS1\033[00;00m "
…ce qui donnera ici un prompt blanc gras. Voici les différentes couleurs disponibles :
COULEUR DÉFAUT GRAS
noir 0;30 1;30
rouge 0;31 1;31
vert 0;32 1;32
jaune 0;33 1;33
bleu 0;34 1;34
magenta 0;35 1;35
cyan 0;36 1;36
blanc 0;37 1;37
Augmenter la taille de l’historique du shell
La capacité de l’historique de Bash est définie par deux variables
d’environnement : $HISTSIZE
et $HISTFILESIZE
; la première indique combien
de commandes sont mises en mémoire à l’ouverture de la session ainsi que combien
seront enregistrées dans l’historique à sa fermeture, et la deuxième combien
d’entrées l’historique peut contenir au maximum (par défaut : 500).
HISTSIZE=1000
HISTFILESIZE=50000
Horodater l’historique des commandes
La commande history
vous permet d’afficher l’historique des commandes
exécutées dans le shell, mais par défaut elle n’affiche pas la date et encore
moins l’heure à laquelle ces commandes ont été exécutées. Pour horodater cet
historique, utilisez la variable d’environnement $HISTTIMEFORMAT
:
export HISTTIMEFORMAT="%d/%m/%Y %H:%M:%S "
À partir de maintenant, votre historique sera horodaté (les entrées dans le
fichier .bash_history
sont préfixées d’un “timestamp” UNIX), et l’output de
la commande history
ressemblera à ça :
$ history | tail -n 5
27 08/05/2009 12:29:15 uname -a
28 09/05/2009 12:29:45 cd /tmp
29 09/05/2009 12:29:46 ls
30 09/05/2009 12:30:05 cd ..
31 09/05/2009 12:30:20 history | tail -n 5
Consultez la page de manuel strftime(3)
pour toute référence aux options de
formatage disponibles.
Réutilisation de commandes avec substitution
Pour ré-exécuter la commande précédente en changeant quelques paramètres, utilisez l'history expansion de Bash :
!!:gs/ancienparamètre/nouveauparamètre/
L'history expansion de Bash est très puissante, et la section du man qui la
documente est à cette image : copieuse. Pour la petite histoire, !!
signifie
qu’on réfère à la ligne d’historique précédente — c’est à dire, la ligne de
commande exécutée juste avant —, :g
signifie qu’on applique la substitution à
toute la ligne de commande (par défaut, seul la première occurrence est
substituée) et enfin le mondialement connu s/foo/bar/
qui demande la
substitution de “foo” par “bar”.
Il est également possible de combiner cette fonctionnalité avec celle qui permet
de ré-exécuter une commande avec la forme !XX
où XX
est le début de la
commande. Par exemple, la commande suivante ré-exécute la dernière occurrence de
scp
en remplaçant “serveur01” par “serveur42” :
!scp:gs/serveur01/serveur42/
Réutiliser certains paramètres de lignes de commande antérieures
Dans le même registre que l’astuce précédente il est possible de réutiliser uniquement certains paramètres de commandes précédemment exécutées, toujours grâce à la fonctionnalité d'history expansion. “Démonstration attention” :
$ mkdir a b c d
$ rmdir !:2-3 # on réutilise uniquement le 2ème et 3ème paramètre de la commande précédente, soit "b" et "c"
rmdir b c
$ ls
a d
Là encore, je vous renvoie vers le man de Bash, section “HISTORY EXPANSION” pour une référence exhaustive de la syntaxe.
Basiquement, on déclare un descripteur de fichier supplémentaire qui pointe vers
un fichier /tmp/monscript-erreurs.log
et on redirige la sortie STDERR
vers
ce nouveau descripteur.
Utiliser une commande aliasée “nature”
Vous vous êtes mitonné un .bash_aliases
aux petits oignons avec tout plein
d’alias pour vous faciliter la vie, et c’est très bien. Mais comment faites-vous
le jour où vous avez justement besoin d’utiliser une de ces commandes sans les
paramètres de l’alias ? Devez-vous supprimer cet alias le temps d’exécuter votre
commande et ensuite le recréer ? Noooonnnn, il suffit d’invoquer la commande
précédée d’un antislash pour inhiber temporairement l’alias et ainsi appeler
la commande “au naturel” :
$ du mon_fichier
6420 mon_fichier
$ alias du='du -h'
$ du mon_fichier
6.3M mon_fichier
$ \du mon_fichier
6420 mon_fichier
Invoquer temporairement un éditeur de texte pour rédiger une ligne de commande longue/complexe
Si vous avez à écrire une ligne de commande particulièrement longue et/ou
complexe, sachez qu’il est possible d’invoquer temporairement l’éditeur de texte
de votre choix (Emacs, Vi[m], Joe…) pour écrire ladite ligne de commande avec
tout le confort que peut vous apporter cet éditeur. Pour ce faire, assurez-vous
que la variable d’environnement $EDITOR
est bien définie au nom du binaire de
votre éditeur favori, et exécutez la combinaison de touches suivante :
<Ctrl-x> <Ctrl-e>
Votre éditeur de texte ouvrira sur un document vierge dans lequel vous pouvez prendre le temps de confectionner votre ligne de commande, cette dernière sera exécutée par le shell après que vous ayez enregistré/quitté l’éditeur.
ℹ️ De la même manière, il est possible d’utiliser la commande fc
pour ouvrir
un éditeur, mais cette fois ci le contenu du fichier sera la dernière commande
exécutée.
Empêcher Bash de logger les commandes exécutées dans l’historique
Dans le cas où vous auriez besoin de tester quelques lignes commandes faisant apparaître des informations sensibles (typiquement, un mot de passe), exécutez au préalable la commande suivante pour rendre Bash amnésique pour la durée de la session :
unset HISTFILE
Toutefois, il est également possible de définir une liste de commande ne devant
jamais être prises en compte par l’historique de Bash. Il suffit de définir pour
cela la variable d’environnement $HISTIGNORE
(chaque entrée devant être
séparée par deux points) :
export HISTIGNORE='ls:ls *'
ℹ️ Les caractères faisant office de jokers dans Bash ("*", “?") peuvent êtres utilisés pour définir des règles d’exclusion plus fines.
Faire des maths avec Bash
Il est possible de procéder dans un shell Bash à quelques opérations
arithmétiques inline à l’aide de la syntaxe
$\![expression arithmétique](/expression arithmétique)
:
$ echo $((2 + 2))
4
$ echo $((((2 * 20) + 3 - 1)))
42
⚠️ Ne fonctionne qu’avec des nombres entiers.
ℹ️ Ne pas confondre avec la syntaxe $(commande)
qui a la même fonction que
`commande`
.
Lire un fichier ligne par ligne
Bash propose une syntaxe pratique pour lire le contenu d’un fichier ligne par ligne :
while read ligne
do
echo $ligne
done <fichier.txt
Exemple qui préfixe toutes les lignes d’un fichier avec la chaîne “PREFIX” :
$ ls -l / > /tmp/fichier.txt
$ while read line; do echo "PREFIX $line"; done < /tmp/fichier.txt
PREFIX total 3906357
PREFIX drwxr-xr-x 2 root root 4096 9 août 17:59 bin
PREFIX drwxr-xr-x 4 root root 1024 5 août 08:53 boot
PREFIX drwxr-xr-x 16 root root 5500 5 août 09:20 dev
PREFIX drwxr-xr-x 60 root root 4096 9 août 17:59 etc
PREFIX drwxr-xr-x 3 root root 4096 6 avril 12:34 home
PREFIX drwxr-xr-x 8 root root 4096 9 août 17:59 lib
…
PREFIX drwxrwxrwt 10 root root 28672 10 août 10:04 tmp
PREFIX drwxr-xr-x 10 root root 4096 9 août 17:59 usr
PREFIX drwxr-xr-x 14 root root 4096 9 août 17:59 var
Exclure certains fichiers lors du listage d’un répertoire
Il est parfois nécessaire de pouvoir boucler sur le contenu d’un répertoire en
excluant certains fichiers ou répertoires, et pour ce faire Bash propose une
alternative pratique au workaround habituel impliquant un filtrage via
grep
/awk
/sed
.
Tout d’abord, il faut activer l’option extglob
de Bash si ce n’est pas déjà
fait :
$ shopt extglob
extglob off
$ shopt -s extglob
$ shopt extglob
extglob on
Il est désormais possible d’exclure des fichiers ou des répertoires en correspondant à un motif donné. Considérons l’exemple d’un répertoire contenant des images de différents formats, pour lequel on souhaite lister toutes les images n’étant pas au format GIF :
$ ls
a.jpg b.jpg c.jpg d.gif e.gif f.png g.png h.png
$ ls !(*gif)
a.jpg b.jpg c.jpg f.png g.png h.png
Ou encore, ne pas lister les images dont le nom de fichier commence par une lettre comprise entre “a” et “c” :
$ ls !([a-c]*)
d.gif e.gif f.png g.png h.png
Il est également possible de déclarer plusieurs motifs à filtrer, par exemple
ici on ne souhaite lister que les fichiers dont le nom ne commence pas par une
lettre comprise entre “a” et “c” et dont l’extension n’est pas .png
:
$ ls !([a-c]*|*png)
d.gif e.gif
ℹ️ Ce mécanisme est récursif par défaut, c’est-à-dire que Bash listera également
le contenu des répertoires contenu dans le répertoire listé, puis des
répertoires que ces répertoires pourraient contenir etc. Pour éviter ce
comportement, ajoutez -d
aux options de votre ls
.
Rediriger simplement la sortie d’erreur vers sortie standard dans un pipe
Lors d’un enchaînement de commandes via pipe
, il est possible rediriger
simplement stderr
vers stdout
en utilisant |&
au lieu de |
– il s’agit
d’un raccourci pour le plus cabalistique 2>&1
. Note : il l’équivalent pour
rediriger vers un descripteur de fichiers existe avec >&
.
Modifier la casse d’une chaîne de caractères variable
Bash est capable d’effectuer des modifications élémentaires au niveau de la casse de chaînes de caractères, par exemple transformer des majuscules en minuscules et inversement :
$ export VAR=coucou
$ echo ${VAR^}
Coucou
$ echo ${VAR^^}
COUCOU
$ export VAR=COUCOU
$ echo ${VAR,}
cOUCOU
$ echo ${VAR,,}
coucou
Développement
Logger (plus élégamment) les messages d’erreur d’un script Bash
Bien qu’il soit possible de rediriger les messages d’erreur au moment de
l’exécution d’un script (du genre script.sh 2>erreurs.log
par exemple), il est
possible de faire cela plus proprement depuis le script :
#!/bin/bash
exec 3>/tmp/monscript-erreurs.log
exec 2>&3
…
exit 0
À noter que pour effectuer du debugging il existe une variante utilisant la
variable BASH_XTRACEFD
permettant de rediriger dans un fichier la sortie de
l’option -x
:
#!/bin/bash
exec 99>/tmp/debug.log
BASH_XTRACEFD=99
set -x
…
exit 0
Utiliser des variables “tableau”
Bash permet de grouper des variables en “tableaux” (arrays) comme dans les langages de programmation évolués, indexés à l’aide d’indices numériques ou par des clés de type alphanumériques chaîne de caractères. Voici quelques exemples d’utilisation :
# Déclaration + assignation d'éléments à un tableau "ARRAY2" (indexés par
# indices numériques) :
declare -a ARRAY1
ARRAY1[0]="toto"
ARRAY1[1]="titi"
ARRAY1[2]="tata"
ARRAY1[3]="tutu"
# Variante :
ARRAY1=(toto titi tata tutu)
# Déclaration + assignation d'éléments à un tableau "ARRAY1" (indexés par clés
# alphanumériques) :
declare -A ARRAY2
ARRAY2["toto"]="TOTO"
ARRAY2["titi"]="TITI"
ARRAY2["tata"]="TATA"
ARRAY2["tutu"]="TUTU"
# Récupérer le nombre d'éléments contenus dans le tableau :
echo ${#ARRAY1[@]} ${#ARRAY2[@]}
4 4
# Récupérer l'intégralité des éléments contenus dans le tableau :
echo ${ARRAY1[@]}
toto titi tata tutu
echo ${ARRAY2[@]}
TUTU TATA TITI TOTO
# Dans le cas des tableaux associatifs, vous pouvez lister les clés
# correspondant aux valeurs comme ceci :
echo ${!ARRAY2[@]}
tutu tata titi toto
Utiliser des valeurs par défaut lors de la déclaration de variables
Lors de l’assignation d’une valeur à une variable, il est possible d’utiliser une valeur par défaut qui sera utilisée si une variable est indéfinie :
# Assigne la valeur 42 à la variable $INDEX si la variable $TOP n'est pas
# définie :
INDEX=${TOP:-42}
echo $INDEX
42
1. Même chose avec la variable $TOP définie :
unset INDEX
TOP=1024
INDEX=${TOP:-42}
echo $INDEX
1024
Modifier une variable à la volée
Si vous avez besoin de modifier temporairement une chaine de caractères contenue dans une variable, Bash propose une syntaxe pratique pour cela :
${VARIABLE#préfixe}
permet de substituer le préfixe préfixe par une autre valeur${VARIABLE%suffixe}
permet de substituer le suffixe suffixe par une autre valeur${VARIABLE/regexp/remplacement}
permet de substituer le motif correspondant à regexp par la chaine remplacement (${VARIABLE//regexp/remplacement}
pour effectuer la substitution sur toutes les occurrences du motif)
Exemple :
FICHIER="repertoire/fichier.txt"
# Renommer le fichier "repertoire/fichier.txt" en "repertoire/fichier.html"
mv $FICHIER ${FICHIER%txt}html
# Déplacer le fichier "repertoire/fichier.txt" dans le répertoire "backup"
mv $FICHIER backup${FICHIER#repertoire}
Découper une chaîne en 2 parties
Petite astuce pour “découper” une chaîne en 2 parties selon un délimiteur connu :
export VAR="hello:world"
export what=${VAR%:*} who=${VAR#*:}
echo "what=$what who=$who"
what=hello who=world
Source : BASH: Split a string without ‘cut’ or ‘awk’
Récupérer l’extension d’un fichier
$ export blah=toto.gz
$ echo ${blah##*.}
gz
Inclure un bloc de texte “tel quel” dans un script
Bash permet d’inclure dans un script un bloc de texte destiné à être affiché “tel quel” :
#!/bin/bash
echo "Voici un bloc Here document :"
cat <<README
==================================================================
Ceci est un bloc de texte simple.
Bash l'affichera tel qu'il est rédigé entre la ligne "cat <<README"
et la dernière ligne ne contenant que le mot suivant le signe "<<"
(ici, "README").
D'ici la fin de bloc de texte (appelé "heredoc", pour "Here
document"), il est possible d'afficher tout ce que l'on veut, de
sauter des lignes et d'indenter son texte librement sans avoir recours
à des caractères de formatage:
* hop
* blah
Les "here documents" sont souvent utilisés pour afficher le bloc
d'aide d'un script (la fameuse option "--help"), ou encore une
licence d'utilisation (GPL, BSD…).
==================================================================
README
# fin du bloc heredoc
exit 0
Étant donné que Bash affiche le heredoc tel qu’il est contenu dans le script, si
vous déclarez un heredoc en plein milieu de code indenté et respectez le niveau
d’indentation courant le texte du heredoc contiendra les tabulations ou espaces
préfixant le texte. Pour éviter ceci, placez le signe “-” (moins) juste entre
les <<
et le mot-stop (dans l’exemple ci-dessus, cela donnerait <<-README
).
Lister les fonctions définies
Lister les noms des fonctions définies
typeset -F
Lister et afficher le code des fonctions définies
typeset -f
Générer une liste de nombres ou de caractères dynamiquement
Bash sait générer des listes alphabétiques dynamiquement avec la syntaxe
{début..fin}
, ce qui permet par exemple d’éviter d’avoir recours à la commande
seq
pour itérer sur des listes numériques basiques. Exemple :
$ echo {1..10}
1 2 3 4 5 6 7 8 9 10
$ echo {1..100..5}
1 6 11 16 21 26 31 36 41 46 51 56 61 66 71 76 81 86 91 96
Des listes alphabétiques sont également possibles :
$ echo {A..Z}
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
$ echo {a..z}
a b c d e f g h i j k l m n o p q r s t u v w x y z
Ressources en ligne
- Sources officielles du shell GNU Bash
- Linux Shell Scripting Tutorial : excellente documentation sur GNU Bash
- Bash Extended Globbing : How-to sur l’extended pattern matching
- Defensive BASH Programming : bonne pratiques pour un développement de scripts plus sûrs