Commandes internes de Bash vs commandes externes : une comparaison des performances
Accélérez les scripts Bash en utilisant les commandes internes du shell pour les tests, l'arithmétique et le traitement de chaînes, tout en regroupant les commandes externes.
Commandes internes de Bash vs commandes externes : une comparaison des performances
Lorsqu'un script Bash semble lent, la cause est souvent une boucle qui lance des milliers de processus externes. Le choix entre commandes internes et externes de Bash est une question pratique de performance : utilisez les fonctionnalités propres au shell pour les tâches simples, et réservez les outils externes pour les travaux qu'ils gèrent mieux.
Ce guide montre où les commandes internes aident, où les commandes externes restent pertinentes, et comment éviter les pièges les plus courants de création de processus.
Comprendre l'exécution des commandes dans Bash
Lorsque Bash rencontre une commande, il résout les alias, les mots-clés du shell, les fonctions, les commandes internes, puis les commandes trouvées via PATH. Cette résolution est importante car tout ce qui est traité à l'intérieur du shell actuel évite de lancer un programme séparé.
1. Commandes internes
Les commandes internes de Bash sont des fonctions implémentées directement dans l'exécutable du shell Bash lui-même. Elles ne nécessitent pas d'invoquer les appels système fork() et exec() du système d'exploitation. Comme l'exécution se fait entièrement dans le processus shell existant, les commandes internes offrent des performances supérieures, une surcharge minimale et un accès immédiat aux variables et à l'état du shell.
Caractéristiques clés des commandes internes :
- Vitesse : Chemin d'exécution le plus rapide.
- Surcharge : Surcharge quasi nulle, car aucun nouveau processus n'est créé.
- Environnement : Elles opèrent directement sur l'environnement du shell actuel.
2. Commandes externes
Les commandes externes sont des fichiers exécutables séparés (souvent situés dans des répertoires comme /bin, /usr/bin, etc.). Lorsque Bash exécute une commande externe, il doit :
fork()un nouveau processus enfant.exec()le programme externe dans ce processus enfant.- Attendre la fin du processus enfant.
Cette surcharge, bien que négligeable pour une seule exécution, s'accumule rapidement dans les boucles ou les opérations à haute fréquence, rendant les commandes externes nettement plus lentes que leurs équivalents internes.
La confrontation des performances : les commandes internes en action
Pour illustrer la différence de performance, considérons des tâches courantes où Bash fournit à la fois une alternative interne et externe.
Exemple 1 : Manipulation de chaînes et calcul de longueur
Calculer la longueur d'une variable est un cas de test de performance classique.
| Type de commande | Commande | Description |
|---|---|---|
| Interne | ${#variable} |
Expansion de paramètre pour la longueur. Extrêmement rapide. |
| Externe | expr length "$variable" |
Invoque l'utilitaire externe expr. Lent. |
Conseil de performance : Utilisez toujours l'expansion de paramètre (${#var}) pour le calcul de longueur plutôt que expr length ou un pipe vers wc -c.
Exemple 2 : Remplacement de chaîne
Remplacer des sous-chaînes dans une variable est une autre opération courante.
| Type de commande | Commande | Description |
|---|---|---|
| Interne | ${variable//pattern/replacement} |
Substitution par expansion de paramètre. Rapide. |
| Externe | sed 's/pattern/replacement/g' |
Invoque l'utilitaire externe sed. Lent. |
Comparaison de code d'exemple :
TEXT="hello world hello"
# Interne (Rapide)
NEW_TEXT_1=${TEXT//hello/goodbye}
# Externe (Lent)
NEW_TEXT_2=$(echo "$TEXT" | sed 's/hello/goodbye/g')
Exemple 3 : Boucles et itération
Lors de l'itération, la commande utilisée à l'intérieur de la boucle a une importance considérable.
| Type de commande | Commande | Description |
|---|---|---|
| Interne | read |
Utilisée pour lire une entrée ligne par ligne efficacement. |
| Externe | grep, awk, cut |
Envoyer des données à des outils externes dans une boucle force la création répétée de processus. |
L'anti-patron while read vs. les commandes internes :
Un modèle lent courant consiste à envoyer le contenu d'un fichier à des commandes externes dans une boucle :
# LENT : Lance 'grep' pour chaque ligne
while read LINE; do
echo "Processing: $LINE" | grep "important"
done < input.txt
Stratégie d'optimisation : Si possible, utilisez les commandes internes de Bash ou la redirection interne pour éviter les commandes externes dans les boucles.
Commandes internes clés de Bash pour la performance
Prioriser ces commandes internes par rapport à leurs équivalents externes apportera des améliorations significatives de vitesse dans vos scripts :
| Catégorie de tâche | Commande interne | Alternative externe (plus lente) |
|---|---|---|
| Arithmétique | (( expression )) |
expr, bc |
| Test de fichier | [[ ... ]] ou [ ... ] interne de Bash |
/usr/bin/test ou /usr/bin/[ externe |
| Manipulation de chaînes | ${var/pat/rep}, ${#var} |
sed, awk, expr |
| Boucle/Lecture de fichier | read |
grep, awk, sed (lorsqu'utilisés de manière itérative) |
| Chargement de code shell | source ou . nom_fichier |
Exécuter un autre script comme processus enfant |
Exemple arithmétique
Interne (Rapide) :
COUNTER=0
(( COUNTER++ ))
if (( COUNTER > 10 )); then echo "Done"; fi
Externe (Lent) :
COUNTER=$(expr "$COUNTER" + 1)
if [ "$COUNTER" -gt 10 ]; then echo "Done"; fi
Quand les commandes externes sont nécessaires
Bien que les commandes internes devraient être le choix par défaut pour les opérations de base, les utilitaires externes restent essentiels pour les tâches que Bash ne peut pas gérer nativement ou efficacement. Vous devez utiliser des commandes externes lorsque :
- Traitement avancé de texte : Correspondance de motifs complexes, manipulation multi-lignes, ou formatage spécifique offert par des outils comme
awk,sed, ouperl. - Utilitaires système : Commandes qui interagissent profondément avec le système d'exploitation, comme
ls,ps,find,mount, ou les outils réseau (curl,ping). - Fichiers externes : Lire ou écrire des fichiers dans des formats complexes avec lesquels la redirection Bash a du mal.
Bonne pratique pour l'utilisation des commandes externes
Si vous devez utiliser une commande externe, essayez de minimiser le nombre de fois qu'elle est invoquée. Au lieu d'exécuter une commande externe dans une boucle, restructurez la logique pour traiter l'ensemble des données en un seul appel externe.
Inefficace : Traiter 1000 fichiers individuellement avec stat.
Efficace : Utiliser un seul appel à find combiné avec stat ou un seul script awk pour rassembler toutes les métadonnées nécessaires en une fois.
À retenir
L'optimisation des performances dans Bash commence par éviter la création inutile de processus. Par défaut, utilisez les commandes internes pour l'arithmétique, les tests et le travail simple sur les chaînes. Lorsqu'un outil externe est le bon outil, exécutez-le une fois sur un lot plutôt qu'une fois par ligne ou par fichier.
- Par défaut, utilisez les commandes internes : Pour l'arithmétique (
(( ))), la manipulation de chaînes (${...}), et les tests ([[ ]]), choisissez toujours la commande interne du shell. - Évitez les E/S dans les boucles : Refactorisez les boucles pour effectuer un traitement par lots en utilisant un seul appel de commande externe plutôt que de nombreux petits appels.
- Utilisez l'expansion de paramètre : Préférez
${#var}àwcouexprpour la longueur de chaîne. - Reconnaissez les compromis : Utilisez les utilitaires externes lorsque la fonctionnalité requise n'est pas disponible ou est maladroite dans Bash.