Plutôt que de chaîner des commandes avec &&
et ||
, on peut utiliser des structures de contrôle: si la commande réussit, le code dans le bloc sera exécuté; sinon, le script passera directement au code qui suit la fin du bloc.
#!/bin/bash
if ls -d /bin; then
echo "Command worked"
fi
Comme la plupart des langages de programmation, il est possible d’ajouter une instruction else:
#!/bin/bash
if ls -d /nop; then
echo "Command worked"
else
echo "Command failed"
fi
Et des else if:
#!/bin/bash
if ls -d /nop; then
echo "Command worked using /nop"
elif ls -d /bin; then
echo "Command worked using /bin"
else
echo "Command failed"
fi
Le deux-points est l’équivalent shell d’un NOP (no op, c’est à dire “pas d’operation”)
if condition
then : # Ne rien faire et continuer
else
faire_quelque_chose
fi
Son état de sortie est vrai (0)
:
echo $? # 0
Le code retour d’une commande est stocké dans la variable $?
Si le code retour vaut 0, c’est que la commande a réussit.
Sinon, qu’une erreur s’est produite (cf exit Codes with Special Meanings)
$ ls -d /bin
/bin
$ echo $?
0
$
$ ls -d /nop
ls: cannot access '/nop': No such file or directory
$ echo $?
2
Si le code retour est 0, alors on entre dans le bloc if
La commande test
permet de tester différentes conditions — par exemple si un fichier existe ou non, si ce fichier est un répertoire, si une variable est vide, si une chaîne de caractère est égale à une autre, etc.
$ test -f /etc/passwd
$ echo $?
0
$
$ test -f /nop
$ echo $?
1
On peut donc se servir de cette commande avec un if
if test $USER = root; then
echo "Hello root"
else
echo "Hello world"
fi
On voit rarement la commande test dans un script shell, à la place on utilise les crochets — ce qui revient à exécuter la commande test. Il est obligatoire d’avoir un espace après la première accolade, et un espace avant la dernière ([ ... ]
et non [...]
).
if [ $USER = root ]; then
echo "Hello root"
else
echo "Hello world"
fi
On peut chaîner les commandes test comme on le ferait d’habitude avec &&
et ||
firstVar="a"
secondVar="b"
if [ -z "$firstVar" ] || [ -z "$secondVar" ]; then
echo "Either firstVar or secondVar is empty"
else
echo "Both variables are defined"
fi
if [ -n "$firstVar" ] && [ -n "$secondVar" ]; then
echo "Both variables are defined"
else
echo "Either firstVar or secondVar is empty"
fi
Une autre possibilité est d’utiliser les opérateurs -o
et -a
à l’intérieur des crochets
if [ -z "$firstVar" -o -z "$secondVar" ]; then
echo "Either firstVar or secondVar is empty"
else
echo "Both variables are defined"
fi
if [ -n "$firstVar" -a -n "$secondVar" ]; then
echo "Both variables are defined"
else
echo "Either firstVar or secondVar is empty"
fi
Et on peut également inverser le résultat d’un test avec !
:
if [ ! -z "$firstVar" ] && [ ! -z "$secondVar" ]; then
echo "Both variables are defined"
else
echo "Either firstVar or secondVar is empty"
fi
Il y a deux manières de grouper des commandes / expressions logiques:
( list )
Placer une liste de commandes entre parenthèses oblige l’interpréteur de commandes à créer un sous-shell.
Les affectations de variables ne restent pas en vigueur après la fin du sous-shell
{ list; }
Placer une liste de commandes entre accolades permet de grouper des commandes sans créer de sous-shell.
Le point-virgule (ou le retour chariot) est obligatoire à la fin de la ligne
{ /bin/dbdump_start.sh && /bin/dbdump_clean.sh; } 2>&1 | logger -p local0.notice -t DB
#!/bin/bash
# Lit les lignes de /etc/fstab.
Fichier=/etc/fstab
{
read ligne1
read ligne2
} < $Fichier
echo "La première ligne dans $Fichier est :"
echo "$ligne1"
echo
echo "La deuxième ligne dans $Fichier est :"
echo "$ligne2"
Dans sa version 2.02, Bash introduit l’expression étendue de test: les doubles crochets — qui est une expression en elle-même et non une commande. Cette syntaxe est supportée par ksh, bash et zsh. L’expression étendue ajoute des fonctionnalités:
&&
, ||
, <
et >
fonctionnent à l’intérieur d’une expression [[ ... ]]
alors qu’ils génèrent une erreur à l’intérieur d’une expression [ ... ]
if [[ -z "$firstVar" || -z "$secondVar" ]]; then
echo "Either firstVar or secondVar is empty"
else
echo "Both variables are defined"
fi
if [[ -n "$firstVar" && -n "$secondVar" ]]; then
echo "Both variables are defined"
else
echo "Either firstVar or secondVar is empty"
fi
=~
permet de vérifier une regex
[[ STRING =~ REGEX ]] | STRING matche la REGEX |
---|
str="Hello World"
if [[ $str =~ ^Hello ]]; then
echo "str says Hello"
fi
true
est une commande qui réussit toujours
false
est une commande qui échoue toujours
$ which true
/bin/true
$ type true
true is a shell builtin
$
$ true
$ echo $?
0
$ false
$ echo $?
1
if true; then
echo "This works"
fi
b=true
if $b; then
echo "This works"
fi
Les doubles parentheses permettent d’évaluer une expression arithmétique.
Si une expression est vraie, son état est 0; et si elle est fausse, son état est 1.
Elles fonctionnent seulement avec Bash, version 2.04 ou ultérieure.
$ var=1
$
$ (( $var > 0 ))
$ echo $?
0
$ (( $var > 2 ))
$ echo $?
1
if (( $var > 0 )); then
echo "var is greater than 0"
else
echo "var is negative or null"
fi
((...))
est une évaluation arithmétique et peut être utilisé dans if if
.
$((...))
est une expansion arithmétique et retourne un résultat qu’on peut afficher / assigner.
$[...]
est l’ancienne syntaxe de l’expansion arithmétique et est dépréciée.
En bash, le code retour 0 indique qu’il n’y a pas d’erreur (= le test est vrai),
et différent de 0 qu’il y a eu une erreur (= le test est faux).
Le résultat d’une comparaison via expansion arithmétique (1 = true, 0 = false)
est l’inverse du code retour de l’évaluation arithmétique (0 = true, 1 = false).
# Résultat Vrai => 1, Faux => 0
$ var=1
$ echo $(( $var > 0 ))
1
$ echo $(( $var > 2 ))
0
$
# Résultat Vrai => 1, Faux => 0
# Code retour Vrai => 0, Faux => 1
$ let "res = (( $var > 0 ))"
$ echo $?
0
$ echo $res
1
$ let "res = (( $var > 2 ))"
$ echo $?
1
$ echo $res
0
Pour une chaîne de caractère: utiliser une substitution de commande avec des opérateurs logiques
a=$([ "$b" == 5 ] && echo "$c" || echo "$d")
Pour un nombre: la syntaxe ? :
est supportée
(( a = b==5 ? c : d ))
ou
a=$(( b==5 ? c : d ))
Permet d’exécuter un bloc de code uniquement lorsque la valeur d’une variable correspond à un motif donné.
case $1 in
"Chien" | "Chat" | "Souris")
echo "C'est un animal"
;;
"Bob")
echo "C'est Bob"
;;
*)
echo "Je ne sais pas ce que c'est"
;;
esac
case $var in
[0-9]*)
echo "$var est un nombre."
;;
[a-zA-Z]*)
echo "$var est un mot."
;;
*)
echo "$var n'est ni un nombre ni un mot."
;;
esac
#!/bin/sh -
option="$1"
fichier="$2"
case $option in
-d) [ -d $fichier ] &&
echo "Repertoire: ’$fichier’";;
-f) [ -f $fichier ] &&
echo "Fichier: ’$fichier’";;
-x) [ -x $fichier ] &&
echo "Executable: ’$fichier’";;
[*])
echo "Un *";;
*)
echo "Option non traitee"
exit 1;;
esac
[ -z STRING ] | Vide (longueur zéro) |
---|---|
[ -n STRING ] | Non vide (longueur non zéro) |
[ STRING1 = STRING2 ] | Égalité |
[ STRING1 != STRING2 ] | Non égalité |
[ STRING1 < STRING2 ] | Dans un tri alphabétique, STRING1 vient avant STRING2 |
[ STRING1 > STRING2 ] | Dans un tri alphabétique, STRING1 vient après STRING2 |
$ empty=""
$ var="value"
$ [ -z "$undefined" ] && echo "yes" || echo "no"
yes
$ [ -z "$empty" ] && echo "yes" || echo "no"
yes
$ [ -z "$var" ] && echo "yes" || echo "no"
no
$ [ -n "$undefined" ] && echo "yes" || echo "no"
no
$ [ -n "$empty" ] && echo "yes" || echo "no"
no
$ [ -n "$var" ] && echo "yes" || echo "no"
yes
if [ -n "$1" ]; then
if [ ! -r "$1" ]; then
echo "Could not read file \"$1\"" >&2
exit 1
fi
echo "Input from file \"$1\""
else
echo "Input from stdin"
fi
[ -e "/home/$USER" ] | Existe |
---|---|
[ -d "/etc" ] | Est un répertoire |
[ -f "/etc/passwd" ] | Est un fichier régulier |
[ -h "/bin/sh" ] | Est un lien symbolique |
[ -b "/dev/sda2" ] | Est un périphérique de type bloc |
[ -c "/dev/ttyS1" ] | Est un périphérique de type caractère |
[ -p "/dev/fd/0" ] | Est un périphérique de type pipe |
[ -S "/var/run/docker.sock" ] | Est un socket |
[ -x FILE ] | Est executable |
[ -r FILE ] | Est accessible en lecture |
[ -w FILE ] | Est accessible en écriture |
[ -s FILE ] | Taille > 0 octets |
[ -g FILE ] | A le flag setgid |
[ -u FILE ] | A le flag setuid |
[ -k FILE ] | A le flag sticky bit |
[ FILE1 -nt FILE2 ] | 1 est plus récent que 2 (newer than) |
[ FILE1 -ot FILE2 ] | 1 est plus ancien que 2 (older than) |
[ FILE1 -ef FILE2 ] | Même fichier (equal file) |
[ -N FILE ] | A une date d'accès inférieure ou égale à la date de modification |
[ -U FILE ] | L'utilisateur en cours est propriétaire du fichier |
[ -G FILE ] | L'utilisateur en cours fait partie du groupe propriétaire du fichier |
# Vérifier si file1.txt existe
if [ -e file1.txt ]; then
echo "File exists"
fi
$ touch test1 test2
$ [ test1 -ef test2 ] && echo "yes" || echo "no"
no
# test3 est un lien symbolique vers test1 = même fichier
$ ln -s test1 test3
$ [ test1 -ef test3 ] && echo "yes" || echo "no"
yes
$ touch test
# Juste crée: accès < modification
$ [ -N test ] && echo "yes" || echo "no"
yes
$
# Juste lu: accès < modification
$ cat test
$ [ -N test ] && echo "yes" || echo "no"
yes
$
# Juste modifié: accès < modification
$ echo "Hello World" > test
$ [ -N test ] && echo "yes" || echo "no"
yes
$
# Date d'accès modifiée: accès > modification
$ touch -a test
$ [ -N test ] && echo "yes" || echo "no"
no
$
# Date de création modifiée: accès < modification
$ touch test
$ [ -N test ] && echo "yes" || echo "no"
yes
[ NUM -eq NUM ] | Égalité |
---|---|
[ NUM -ne NUM ] | Non égalité |
[ NUM -lt NUM ] | Inférieur à |
[ NUM -le NUM ] | Inférieur ou égal à |
[ NUM -gt NUM ] | Supérieur à |
[ NUM -ge NUM ] | Supérieur ou égal à |
ls -d /nop
if [ $? -eq 0 ]; then
echo "Last command succeeded"
else
echo "Last command failed"
fi
[ ! EXPR ] | Non |
---|---|
[ EXPR1 -a EXPR2 ] | Et |
[ EXPR1 -o EXPR2 ] | Ou |
if [ -f "/usr/bin/netscape" -a -f "/usr/share/doc/HTML/index.html" ]; then
netscape /usr/share/doc/HTML/index.html &
fi