0
votes

Bash - le moyen le plus rapide de faire une correspondance de chaîne entière sur des éléments de tableau?

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 commentaires

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!


3 Réponses :


2
votes

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.


2 commentaires

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.



1
votes

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.


3 commentaires

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.



0
votes

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)


7 commentaires

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 ...