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 :

  1. fork() un nouveau processus enfant.
  2. exec() le programme externe dans ce processus enfant.
  3. 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 :

  1. Traitement avancé de texte : Correspondance de motifs complexes, manipulation multi-lignes, ou formatage spécifique offert par des outils comme awk, sed, ou perl.
  2. 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).
  3. 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} à wc ou expr pour 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.