J'ai un tableau bash (appelé tenantlist_array
ci-dessous) tenantlist_array
d'éléments au format suivant:
{3 characters}-{3-5 characters}{3-5 digits}-{2 chars}{1-2 digits}
.
Exemple:
real 2m8.618s user 0m0.906s sys 0m48.094s
Les éléments du tableau sont uniques. Veuillez noter le trait d'union, il fait partie de chaque élément du tableau.
Je dois vérifier si la chaîne fournie (utilisée dans l'exemple ci-dessous en tant que tenant
variable) produit une correspondance complète avec n'importe quel élément du tableau - parce que les éléments du tableau sont uniques, la première correspondance est suffisante.
J'itère sur les éléments du tableau en utilisant le code simple:
real 1m2.541s user 0m0.500s sys 0m24.063s
Veuillez noter - j'ai besoin d'une correspondance de chaîne complète - si, par exemple, la chaîne que je recherche est hac101
elle ne doit correspondre à aucun élément du tableau, même si peut être une sous-chaîne si un élément du tableau.
En d'autres termes, seule la chaîne complète abc-hac101-bb0
doit produire la correspondance avec le premier élément. Les chaînes abc
, abc-hac
, b2b
, 99
, -
ne doivent pas produire la correspondance. C'est pourquoi le paramètre -x
est associé à l'appel grep.
Maintenant, le code ci-dessus fonctionne, mais je le trouve assez lent. Je l'ai exécuté avec le tableau contenant 193 éléments et sur un ordinateur portable ordinaire, il faut près de 90 secondes pour parcourir les éléments du tableau:
tenant="$1" for k in "${tenantlist_array[@]}"; do result=$(grep -x -- "$tenant" <<<"$k") if [[ $result ]]; then break fi done
Et avec les 385 éléments du tableau, le temps est suivant:
abc-hac101-bb0 xyz-b2blo97250-aa99 abc-b2b9912-xy00 fff-hac101-g3
Alors ma question - y a-t-il un moyen plus rapide de le faire?
3 Réponses :
Sans exécuter de boucle, vous pouvez le faire en utilisant glob:
tenant="$1" [[ $(printf '\3%s\3' "${tenantlist_array[@]}") == *$'\3'"$tenant"$'\3'* ]] && echo "ok" || echo "no"
Dans printf
nous printf
un caractère de contrôle \3
autour de chaque élément et en comparant nous nous assurons de placer \3
avant et après la clé de recherche.
Permettez-moi d'essayer d'expliquer encore une fois parce qu'il semble qu'il y ait un malentendu. J'ai déjà la chaîne de recherche dans ma main - je n'ai pas besoin de vérifier si cette chaîne confirme l'exigence des éléments du tableau. Ce dont j'ai besoin est de vérifier si cette chaîne correspond à 1: 1 pour n'importe quel élément du tableau.
Je pense que c'était clair dans cette description: «Je dois vérifier si la chaîne fournie produit une correspondance complète avec l'élément du tableau.
Grâce à @ arco444 , la solution est étonnamment simple:
real 0m0.007s user 0m0.000s sys 0m0.000s
Et la différence de départ pour le tableau de 385 membres:
tenant="$1" for k in "${tenantlist_array[@]}"; do if [[ $k = "$tenant" ]]; then result="$k" break fi done
Mille fois plus vite.
Cela donne une idée du gaspillage d'appels à grep, ce qui doit être évité, si possible.
Il y a une faute de frappe dans le code: ce devrait être ==
pas =
. De plus, bash est vraiment lent avec ce genre de trucs ... pensez à utiliser awk ou encore plus simple grep.
@RiccardoPetraglia ce n'est pas une faute de frappe. Voir stackoverflow.com/questions/20449543/… . Veuillez également lire attentivement la question. OP a déjà déclaré avoir un tableau dans bash
et l'utilisation de grep
dans le script causait des problèmes de performances.
@ arco444 Merci de m'avoir indiqué l'explication sur =
et ==
qui a été très utile! À propos de grep
: leur méthode est lente car ils utilisent grep d'une manière complètement erronée. Je vais ajouter une réponse avec la façon dont j'utiliserais grep.
C'est une manière alternative d'utiliser grep
qui utilise en fait grep
au maximum de sa puissance.
Le code pour "formater" le tableau pourrait être complètement supprimé en ajoutant simplement un \n
à la fin de chaque chaîne uuid lors de la création du tableau la première fois.
Ce code se dégraderait également beaucoup plus lentement avec la longueur des chaînes comparées et avec la longueur du tableau.
tenant="$1" formatted_array="" for k in "${tenantlist_array[@]}"; do formatted_array="$formatted_array $i\n" done result=$(echo -e "$formatted_array" | grep $tenant)
Quelques problèmes ici. 1. Il n'y a pas grand intérêt à boucler le tableau uniquement dans le but de construire une chaîne afin que vous puissiez la grep
, vous pouvez aussi bien vérifier la valeur voulue dans la boucle et casser, comme OP le fait déjà. 2. $result
contiendra la chaîne entière, pas l'élément souhaité. Si vous voulez une solution grep
, vous pouvez ignorer la boucle et dire result=$(echo -e ${tenantlist_array[@]} | grep -o "$tenant")
@ arco444 ok pour le point 1. mais, comme je l'ai dit dans la réponse, vous pouvez construire le tableau avec un suffixe \n
et je pense que nous sommes tous les deux d'accord sur le fait que les performances de grep
sont supérieures à celles de la comparaison de chaînes avec bash . À propos du point 2, c'est probablement la solution la meilleure et la plus rapide.
Je ne suis certainement pas d'accord sur des performances supérieures dans ce cas - c'est-à-dire engendrer un sous-shell pour exécuter un processus pour vérifier la valeur d'une variable qui est déjà dans la mémoire du processus parent. Faire la vérification sans grep est presque certainement plus rapide. Quoi qu'il en soit, toute performance supérieure est totalement perdue en utilisant une boucle et en ne la quittant pas le plus tôt possible.
J'ai dit: "les performances de grep sont supérieures aux performances de comparaison de chaînes avec bash". Vous pouvez faire n'importe quel test que vous aimez à ce sujet ... cela pourrait dépendre de la longueur de la chaîne et de la longueur du tableau mais ... allez ... Vous ne pouvez pas vraiment discuter de cela.
"Quoi qu'il en soit, toute performance supérieure est totalement perdue en utilisant une boucle et en ne la quittant pas le plus tôt possible" Si vous faites référence à ma boucle pour ajouter le \n
c'est pourquoi j'ai dit que l'utilisation de grep
de la manière que vous avez proposée était la meilleure solution. Néanmoins, si vous pouvez ajouter le \n
lors de la construction du tableau, les performances seront meilleures que la comparaison de chaînes avec bash (pour des chaînes "assez longues").
Je pense que le résultat que j'ai publié lorsque j'ai éliminé l'appel grep parle de lui-même. J'ai affaire à un tableau «juste» de 300 membres, mais dans l'environnement réel, il peut s'agir de plusieurs milliers de membres du tableau et la chaîne que je vais associer pourrait être sur le premier élément. J'ai besoin que le code s'exécute rapidement et tout ce qui vise cet objectif est meilleur.
@ Invisible999 grep
est la plupart du temps plus efficace qu'une comparaison bash, surtout si la quantité est grande (sinon grep serait juste un script bash au lieu d'un logiciel c complexe). Comme le suggère arco444, essayez result=$(echo -e ${tenantlist_array[@]} | grep -o "$tenant")
au lieu de if ...
Si vous avez besoin d'une correspondance de chaîne complète, vous n'avez pas besoin d'utiliser une expression régulière (même si vous le faites, vous n'avez pas besoin de générer un sous-shell / d'utiliser grep). Vous devriez pouvoir simplement tester l'égalité
if [[ "$1" = "$k" ]]; then ...
@ arco444 vous avez raison! Question - voyez-vous des inconvénients potentiels?
Non, je ne vois aucun inconvénient - l'opération est littéralement destinée à cet effet!