Comparaison des conditionnelles Bash : quand utiliser test, [ et [[
Comparez test, les crochets simples et les crochets doubles pour que vos conditionnelles Bash restent portables, sûres et lisibles.
Comparaison des conditionnelles Bash : quand utiliser test, [ et [[
Lorsqu'une conditionnelle Bash se comporte étrangement, le problème vient souvent de la construction que vous avez choisie. test, [ ] et [[ ]] se ressemblent, mais ils gèrent les guillemets, les motifs, les expressions régulières et la portabilité différemment.
Ce guide compare les trois formes afin que vous puissiez écrire des conditionnelles sûres, lisibles et adaptées au shell sous lequel votre script s'exécute réellement.
La commande test : les fondations
La commande test est l'une des plus anciennes et des plus fondamentales pour évaluer des conditions dans les scripts shell. C'est une commande intégrée dans la plupart des shells modernes et fait partie de la norme POSIX, ce qui la rend très portable. test évalue une expression et renvoie un code de sortie de 0 (vrai) ou 1 (faux).
Utilisation de base
La commande test prend un ou plusieurs arguments, qui forment l'expression à évaluer. Elle vérifie les attributs de fichiers, les comparaisons de chaînes et les comparaisons d'entiers.
# Vérifier si un fichier existe
if test -f "monfichier.txt"; then
echo "monfichier.txt existe et est un fichier régulier."
fi
# Vérifier si deux chaînes sont égales
NOM="Alice"
if test "$NOM" = "Alice"; then
echo "Le nom est Alice."
fi
# Vérifier si un nombre est supérieur à un autre
COMPTEUR=10
if test "$COMPTEUR" -gt 5; then
echo "Le compteur est supérieur à 5."
fi
Opérateurs courants de test
- Opérateurs de fichier :
-f(fichier régulier),-d(répertoire),-e(existe),-s(non vide),-r(lisible),-w(inscriptible),-x(exécutable). - Opérateurs de chaîne :
=(égal),!=(différent),-z(chaîne vide),-n(chaîne non vide). - Opérateurs d'entier :
-eq(égal),-ne(différent),-gt(supérieur à),-ge(supérieur ou égal),-lt(inférieur à),-le(inférieur ou égal).
Astuce : Mettez toujours les variables utilisées avec test entre guillemets (par exemple "$NOM") pour éviter les problèmes de découpage des mots et d'expansion des chemins si la valeur de la variable contient des espaces ou des caractères génériques.
Les crochets simples [ ] : la forme test
La construction avec crochets simples [ ] est une syntaxe alternative pour la commande test. Dans de nombreux shells, [ est une commande intégrée, et les systèmes fournissent souvent également un /usr/bin/[ externe. La différence clé est que [ nécessite un ] de fermeture comme dernier argument. Comme test, il est conforme à POSIX.
Syntaxe et sémantique
# Équivalent à test -f "monfichier.txt"
if [ -f "monfichier.txt" ]; then
echo "monfichier.txt existe et est un fichier régulier en utilisant [ ]."
fi
# Équivalent à test "$NOM" = "Alice"
NOM="Bob"
if [ "$NOM" != "Alice" ]; then
echo "Le nom n'est pas Alice."
fi
Remarquez l'espace obligatoire après [ et avant ]. Ceux-ci sont traités comme des arguments séparés pour la commande [.
Mise entre guillemets des variables : un détail critique
Parce que [ ] est fondamentalement la commande test, il hérite des mêmes comportements concernant le découpage des mots et l'expansion des chemins. Cela signifie que les variables non mises entre guillemets peuvent entraîner un comportement inattendu ou des vulnérabilités de sécurité.
Considérez cet exemple :
#!/bin/bash
ENTREE="fichier avec espaces.txt"
# DANGEREUX : Une variable non mise entre guillemets causera des problèmes si ENTREE contient des espaces
# Le shell effectuera un découpage des mots, traitant "fichier" et "avec espaces.txt" comme des arguments séparés
# conduisant à une erreur de syntaxe ou à une évaluation incorrecte.
# if [ -f $ENTREE ]; then echo "Trouvé"; else echo "Non trouvé"; fi
# CORRECT : Mettez la variable entre guillemets pour la traiter comme un seul argument
if [ -f "$ENTREE" ]; then
echo "'fichier avec espaces.txt' existe."
else
echo "'fichier avec espaces.txt' n'existe pas ou n'est pas un fichier régulier."
fi
Sans guillemets, $ENTREE se développerait en fichier avec espaces.txt, et [ -f fichier avec espaces.txt ] serait interprété comme une erreur de syntaxe par la commande [ car -f attend un seul opérande. La mise entre guillemets garantit que $ENTREE est passé comme un seul argument, "fichier avec espaces.txt".
Dangers du découpage des mots et de l'expansion des chemins
test et [ sont tous deux sujets aux comportements par défaut du shell de découpage des mots et d'expansion des chemins (globbing). Si une variable contient des espaces ou des caractères génériques (*, ?, [ ]) et n'est pas mise entre guillemets, le shell l'expandera avant que test ou [ ne voient les arguments. Cela peut conduire à des erreurs de syntaxe ou à des comparaisons incorrectes lorsque les caractères génériques correspondent à des fichiers existants.
Les crochets doubles [[ ]] : le mot-clé Bash moderne
La construction avec crochets doubles [[ ]] est un mot-clé Bash (également supporté par Ksh et Zsh), pas une commande externe ou un alias. Cette distinction est cruciale, car elle permet à [[ ]] de se comporter différemment et d'offrir des fonctionnalités améliorées et une sécurité accrue par rapport à test ou [ ].
Fonctionnalités améliorées
[[ ]] introduit plusieurs fonctionnalités puissantes non disponibles avec test ou [ :
Pas de découpage des mots ni d'expansion des chemins : Les variables à l'intérieur de
[[ ]]n'ont généralement pas besoin d'être mises entre guillemets (bien que ce soit souvent une bonne pratique pour la clarté). Le shell traite le contenu de[[ ]]comme une seule unité, empêchant le découpage des mots et l'expansion des chemins. Cela réduit considérablement les erreurs de script courantes et les risques de sécurité.# Pas besoin de mettre les variables entre guillemets (bien que ce soit toujours sûr de le faire) ENTREE="fichier avec espaces.txt" if [[ -f $ENTREE ]]; then # $ENTREE est traité comme une seule chaîne ici echo "'$ENTREE' existe." fiGlobbing pour la comparaison de chaînes : Les opérateurs
==et!=effectuent une correspondance de motifs (globbing) plutôt qu'une égalité stricte de chaînes lorsqu'ils sont utilisés à l'intérieur de[[ ]]. Cela signifie que vous pouvez utiliser*,?et[]comme caractères génériques.NOM_FICHIER="mon_document.txt" if [[ "$NOM_FICHIER" == *".txt" ]]; then # Vérifie si NOM_FICHIER se termine par .txt echo "C'est un fichier texte !" fi # Remarque : Pour une égalité stricte de chaînes sans globbing, utilisez `test` ou `[ ]` avec `=` # ou assurez-vous qu'aucun caractère générique n'est présent dans le côté droit de `==` dans [[ ]] # (ou mettez le côté droit entre guillemets s'il contient des caractères génériques littéraux que vous voulez faire correspondre littéralement).Correspondance d'expressions régulières : L'opérateur
=~vous permet d'effectuer une correspondance d'expressions régulières.
ADRESSE_IP="192.168.1.100"
if [[ "$ADRESSE_IP" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
echo "Format IP valide."
fi
# Important : Le motif regex du côté droit de =~ ne doit généralement PAS être mis entre guillemets
# s'il contient des caractères qui seraient autrement traités comme des motifs globbing.
# Si la regex est dans une variable, elle doit également être non mise entre guillemets.
# Exemple de motif : ^[A-Za-z]+$
```
4. **Opérateurs logiques `&&` et `||`** : `[[ ]]` supporte les opérateurs logiques de style C plus intuitifs `&&` (ET) et `||` (OU) pour combiner plusieurs conditions, ainsi que `!` pour la négation. Ces opérateurs ont une évaluation de court-circuit et une précédence appropriées, contrairement à `-a` et `-o` de `test`.
```bash
AGE=25
if [[ "$NOM" == "Alice" && "$AGE" -ge 18 ]]; then
echo "Alice est adulte."
fi
if [[ "$UTILISATEUR" == "root" || -w /etc/fstab ]]; then
echo "Soit root, soit peut écrire dans fstab."
fi
```
### Nature spécifique à Bash
Bien que `[[ ]]` offre des avantages significatifs, son principal inconvénient est qu'il s'agit d'une extension Bash/Ksh/Zsh et qu'il ne fait pas partie de la norme POSIX. Cela signifie que les scripts reposant sur `[[ ]]` peuvent ne pas être portables vers `sh`, `dash` ou des systèmes de type Unix anciens/minimalistes.
## Comparaison côte à côte : `test` vs. `[` vs. `[[`
Voici un tableau résumant les principales différences :
| Fonctionnalité | `test` | `[ ]` | `[[ ]]` |
| :------------------------- | :------------------------------- | :---------------------------------- | :---------------------------------------- |
| **Type** | Commande intégrée (ou externe) | Commande intégrée (alias de `test`) | Mot-clé du shell (Bash, Ksh, Zsh) |
| **Conforme POSIX** | Oui | Oui | Non |
| **`]` de fermeture requis** | Non | Oui (comme dernier argument) | Oui (dans le cadre du mot-clé) |
| **Découpage des mots** | Oui, sur les variables non mises entre guillemets | Oui, sur les variables non mises entre guillemets | Non, les variables sont traitées comme des chaînes uniques |
| **Expansion des chemins** | Oui, sur les variables non mises entre guillemets | Oui, sur les variables non mises entre guillemets | Non |
| **Correspondance de motifs Globbing** | Non pour l'égalité de chaînes | Non pour l'égalité de chaînes | Oui avec le côté droit non mis entre guillemets de `==` ou `!=` |
| **Expressions régulières** | Non | Non | Oui avec `=~` |
| **ET/OU logiques** | `-a`, `-o` existent mais sont faciles à mal interpréter | `-a`, `-o` existent mais sont faciles à mal interpréter | `&&`, `||` avec comportement de court-circuit normal |
| **Commandes composées** | Nécessite des appels `test` séparés | Nécessite des appels `[` séparés | Peut combiner des expressions directement (`&&`/`||`)|
| **Mise entre guillemets des variables** | **Obligatoire** pour la sécurité | **Obligatoire** pour la sécurité | Généralement pas requis, mais bonne pratique |
## Quand utiliser quoi
Choisir la bonne construction conditionnelle dépend principalement de vos exigences de portabilité et de la complexité de votre logique conditionnelle.
### Conformité POSIX vs. Fonctionnalités Bash modernes
- **Utilisez `test` ou `[ ]` lorsque...**
- **La portabilité est primordiale** : Si votre script doit s'exécuter sur n'importe quel shell conforme POSIX (`sh`, `dash`, systèmes plus anciens, etc.), `test` ou `[ ]` sont vos seules options fiables.
- Vos conditions sont simples (vérifications de fichiers, comparaisons de base de chaînes/entiers).
- Vous êtes à l'aise avec la mise entre guillemets minutieuse de toutes les variables et l'utilisation de `&&`/`||` au niveau du shell en dehors des crochets lorsque vous avez besoin d'une logique composée.
- **Utilisez `[[ ]]` lorsque...**
- **Vous écrivez exclusivement pour Bash** (ou Ksh/Zsh) et n'avez pas besoin de portabilité POSIX.
- Vous avez besoin de fonctionnalités avancées comme la correspondance de motifs globbing, la correspondance d'expressions régulières ou les opérateurs logiques de style C `&&`/`||`.
- Vous voulez les fonctionnalités de sécurité améliorées qui empêchent le découpage des mots et l'expansion des chemins, conduisant à un code plus robuste et moins sujet aux erreurs.
- Vos conditions impliquent une logique complexe qui serait lourde avec `test -a`/`-o`.
### Bonnes pratiques et recommandations
1. **Priorisez `[[ ]]` pour les scripts Bash** : Si votre script est destiné à Bash, `[[ ]]` est généralement le choix préféré en raison de sa sécurité accrue, de ses fonctionnalités étendues et de sa syntaxe plus intuitive pour les conditions complexes. Il réduit considérablement les erreurs de script courantes liées aux guillemets et aux caractères spéciaux.
2. **Mettez toujours entre guillemets dans `test` et `[ ]`** : Si vous *devez* utiliser `test` ou `[ ]` pour la conformité POSIX, prenez l'habitude de **toujours mettre vos variables entre guillemets** pour éviter les comportements inattendus dus au découpage des mots et à l'expansion des chemins.
```bash
# Bonne pratique pour [ ] et test
VAR="une chaîne avec espaces"
if [ -n "$VAR" ]; then echo "Pas vide"; fi
```
3. **Soyez attentif à la correspondance de motifs** : Dans `test` et `[ ]`, `=` est utilisé pour l'égalité de chaînes. Dans `[[ ]]`, `=` et `==` peuvent tous deux effectuer une correspondance de motifs lorsque le côté droit n'est pas mis entre guillemets. Mettez le côté droit entre guillemets lorsque vous voulez une comparaison de chaînes littérale.
4. **Expressions régulières avec `=~`** : Lorsque vous utilisez `=~` dans `[[ ]]`, le côté droit ne doit généralement pas être mis entre guillemets pour permettre au shell de l'interpréter comme un motif d'expression régulière, et non comme une chaîne littérale à faire correspondre.
```bash
# Le motif regex non mis entre guillemets est correct pour =~ dans [[ ]]
if [[ "$LIGNE" =~ ^Erreur: ]]; then echo "Erreur trouvée"; fi
```
## À retenir
Utilisez `[ ]` ou `test` lorsque votre script doit s'exécuter sous POSIX `sh`. Utilisez `[[ ]]` lorsque votre shebang est Bash et que vous voulez une gestion plus sûre des variables, la correspondance globbing, la correspondance regex et des conditions composées plus propres. L'habitude principale est simple : faites correspondre la syntaxe conditionnelle au shell, et mettez entre guillemets de manière délibérée.