Un guide pour utiliser la correspondance de modèles Bash au lieu de grep et sed
Vous avez probablement déjà écrit des dizaines de scripts Bash. Vous avez peut-être utilisé grep dans une instruction conditionnelle ou sed pour transformer de petits blocs de texte en ligne. Et si je vous disais que c'est incroyablement inefficace ? Bash fournit des utilitaires intégrés pour gérer ces cas, mais ils sont rarement utilisés, alors réparons ce problème.
Je l'ai fait moi-même (depuis des années) en utilisant des outils externes où Bash fait mieux le travail. Ce n'est pas une exagération ; il existe de réels gains de performances, et il s’agit souvent d’une bonne pratique. La correspondance de modèles Bash est simple et plus que adaptée la plupart du temps. Je vais expliquer pourquoi grep et sed sont lents, quelles sont les alternatives et comment vous les utilisez.
Sommaire
8 astuces du shell Linux qui changent complètement le fonctionnement des commandes
Le shell fait bien plus que simplement exécuter des commandes. Voici comment Bash étend votre saisie en coulisse afin que vous puissiez écrire des commandes plus propres et plus fiables.
Pourquoi ne pas utiliser grep et sed ?
grep et sed sont tous deux des outils formidables qui ont tenu bon pendant des décennies, mais lorsqu'ils sont mal utilisés, ils causent des problèmes.
Pour illustrer le problème principal, analysons 10 000 appels grep successifs :
time for ((i=0; i<10000; i++)); do
echo 'Hello, World!' | grep 'Hello' >/dev/null
done
Et pour 10 000 appels sed :
time for ((i = 0; i < 10000; i++)); do
echo 'Hello, World!' | sed 's/Hello/Goodbye/' >/dev/null
done
Comparez-les à leurs équivalents Bash qui utilisent la correspondance de modèles (couvert ensuite). Ce qui suit est un pur substitut grep de Bash :
time for ((i = 0; i < 10000; i++)); do
(( 'Hello, World!' == *Hello* )) && true
done
Et un pur substitut Bash sed :
str='Hello, World!'
time for ((i = 0; i < 10000; i++)); do
result=${str/Hello/Goodbye}
done
Grep et sed sont plus lents car chaque appel à un binaire externe nécessite :
-
Un fork du processus Bash actuel, le dupliquant
-
Un remplacement complet du processus enfant (en utilisant execve) par l'exécutable souhaité
Ce processus nécessite beaucoup de ressources.
Les exemples fournis ne sont pas représentatifs de toutes les charges de travail. Souvent, grep et sed analysent des fichiers entiers en masse, ce dans quoi ils excellent. Cependant, à mesure que les tâches diminuent et que les appels deviennent plus fréquents, leur efficacité diminue.
Qu’est-ce que la correspondance de modèles Bash ?
La correspondance de modèles, comme son nom l'indique, fait correspondre les chaînes aux représentations souhaitées. Il s'agit d'une fonctionnalité native de Bash, ce qui signifie que Bash n'utilise pas de forks coûteux pour l'exécuter.
Un modèle de base ressemble à :
Hello*
Cela signifie : faites correspondre tout ce qui commence par « Bonjour ». Vous avez probablement déjà utilisé un astérisque comme celui-ci, mais il reste encore beaucoup à explorer.
La correspondance de modèles est couramment utilisée dans les instructions switch :
value="Hello, World!"
case "$value" in
Hello*) echo "matched" ;;
*) echo "I match anything not matched";;
esac
Les modèles peuvent également être utilisés dans les instructions conditionnelles :
(( "Hello, World!" == Hello* )) && echo "matched"
Vous pouvez remplacer grep pour les cas simples en utilisant les deux approches.
Remarquez que « Bonjour* » n'est pas une chaîne ? Si vous mettez des guillemets autour, Bash essaie de le faire correspondre littéralement, ce que nous ne voulons pas. De plus, pour les instructions conditionnelles, vous devez utiliser des doubles crochets (((...))) lors de la correspondance de motifs.
Et pour remplacer du texte (comme le fait sed), nous pouvons utiliser l'expansion des paramètres :
str='Hello, World!'
echo "${str/Hello/Goodbye}"
L'expansion des paramètres signifie simplement transformer une variable en valeur. Par exemple, $foo se développe dans sa valeur assignée. Autre exemple : si var="Foo"alors "${var/Foo/Bar}" se développe en « Bar ». C'est un processus effectué par Bash avant d'exécuter le script.
Une introduction aux bases de la correspondance de modèles
La forme la plus basique de correspondance de modèles utilise des caractères génériques, et vous les avez probablement déjà utilisés :
ls foo*
-
*: Correspond à n'importe quelle chaîne, par exemple,Hello*correspond à « Bonjour le monde ! » -
?: Correspond à n'importe quel caractère, par exemple,H?llocorrespond à Hallo, H-llo, etc.
Cela couvre les caractères génériques POSIX standard, mais Bash propose trois extensions supplémentaires. Les expressions entre crochets vous permettent de spécifier des caractères individuels à faire correspondre :
(a-z)
Cela correspondra à n’importe quel caractère de « a » à « z ». Quelques autres exemples :
-
(A-Z): fait correspondre n'importe quel caractère majuscule. -
(a-zA-Z): fait correspondre n'importe quel caractère minuscule ou majuscule. -
(0-9): correspond à n'importe quel chiffre. -
("£$%^&*()): Faites correspondre l'un de ces caractères spéciaux une fois. -
(^a-z): annuler une correspondance : faire correspondre n'importe quel caractère qui n'est pas une lettre minuscule de « a » à « z ».
Vous pouvez créer votre propre motif et également le mélanger avec des caractères génériques : (a-z)(-_0-9)*.
Ensuite, une classe de caractères nous permet de faire correspondre du texte indépendant des paramètres régionaux, ce qui signifie essentiellement qu'elle fonctionnera avec des caractères non-ASCII.
(:alnum:)
Cela correspondra à n’importe quel caractère alphanumérique, par exemple. Il existe 12 classes de caractères POSIX standard.
Pour utiliser une classe de caractères avec correspondance de motifs, vous devez l'intégrer dans une expression entre crochets : ()-c'est-à-dire, enveloppez la classe de caractères avec une autre paire de crochets : ((:alnum:)). Vous pouvez développer davantage l'expression entre parenthèses en utilisant ce que vous avez appris :
(a-z(:digit:))
La correspondance de modèles en action
J'ai déjà abordé la correspondance de modèles dans les instructions conditionnelles, les instructions case et les extensions de paramètres, je vais donc me plonger dans un exemple plus complexe.
(( 'Hello, World!' == (Hh)ello?((:space:))W* )) && echo 'matched'
La chaîne correspondante doit commencer par « Bonjour » (en majuscule ou non) et doit être suivie de…
-
N'importe quel caractère unique (
?). -
Un espace (
((:space:))). -
Un « W » littéral.
-
N'importe quelle chaîne (
*), dont rien.
Voici quelques exemples de correspondances :
-
Bonjour; En-cours
-
Bonjour_ Oups
-
Bonjour & W
La seule chose que je n'ai pas incluse dans cet exemple est (a-z)mais je suis sûr que vous pouvez imaginer où cela pourrait s'intégrer. Essayez-le et voyez.
3 techniques de script Bash que tout utilisateur Linux devrait connaître
Libérez la puissance de Bash avec ces techniques simples.
Pour une utilisation unique, l'utilisation de grep et sed en ligne est correcte mais inefficace, car votre système doit créer et remplacer le processus forké, ce qui nécessite un travail considérable. Le programme doit initialiser et détruire toutes ses structures de données, ce qui représente beaucoup d'efforts inutiles. En revanche, une fonctionnalité native de Bash est simplement une construction au sein d’un programme déjà en cours d’exécution. Une analogie appropriée serait de louer un appartement chaque fois que vous avez besoin de vous doucher. Même si c’est abordable, cela n’a toujours pas de sens. Ces outils n'ont de sens que lors du traitement de gros volumes de texte.
Une autre erreur courante consiste à utiliser ces programmes pour leur puissant moteur d'expression régulière, car Bash le prend déjà en charge avec le =~ opérateur.
En conclusion, la syntaxe de correspondance de modèles est plus propre et plus idiomatique (conventionnelle et meilleure pratique). À moins que vous ne traitiez de gros volumes de données, préférez plutôt les fonctionnalités natives de Bash.
3 bonnes raisons de remplacer Bash par Zsh
Zsh est plus avancé que Bash et a beaucoup plus de potentiel pour augmenter le débit de votre terminal.
