Quel est l'équivalent des dictionnaires Python mais dans Bash (devrait fonctionner sous OS X et Linux).
bash
dictionary
hashtable
associative-array
Sridhar Ratnakumar
la source
la source
Réponses:
Bash 4
Bash 4 prend nativement en charge cette fonctionnalité. Assurez-vous que le hashbang de votre script est
#!/usr/bin/env bash
ou non#!/bin/bash
, vous ne finissez pas par l'utilisersh
. Assurez-vous que vous exécutez votre script directement ou exécutezscript
avecbash script
. (Non exécution en fait un script Bash Bash ne se produit, et sera vraiment confus!)Vous déclarez un tableau associatif en faisant:
Vous pouvez le remplir d'éléments en utilisant l'opérateur d'affectation de tableau normal. Par exemple, si vous souhaitez avoir une carte de
animal[sound(key)] = animal(value)
:Ou fusionnez-les:
Ensuite, utilisez-les comme des tableaux normaux. Utilisation
animals['key']='value'
pour définir la valeur"${animals[@]}"
pour étendre les valeurs"${!animals[@]}"
(remarquez le!
) pour développer les touchesN'oubliez pas de les citer:
Bash 3
Avant bash 4, vous n'avez pas de tableaux associatifs. Ne les utilisez pas
eval
pour les émuler . Évitezeval
comme la peste, car il est le fléau de scripts shell. La raison la plus importante est queeval
vos données sont traitées comme du code exécutable (il existe également de nombreuses autres raisons).Tout d'abord : envisagez la mise à niveau vers bash 4. Cela rendra le processus beaucoup plus facile pour vous.
S'il y a une raison pour laquelle vous ne pouvez pas mettre à niveau,
declare
c'est une option beaucoup plus sûre. Il n'évalue pas les données comme le fait le code basheval
, et en tant que tel ne permet pas l'injection de code arbitraire aussi facilement.Préparons la réponse en introduisant les concepts:
Tout d'abord, l'indirection.
Deuxièmement
declare
:Rassemblez-les:
Utilisons-le:
Remarque:
declare
ne peut pas être mis dans une fonction. Toute utilisation de l'declare
intérieur d'une fonction bash transforme la variable qu'elle crée localement en portée de cette fonction, ce qui signifie que nous ne pouvons pas accéder ou modifier les tableaux globaux avec elle. (Dans bash 4, vous pouvez utiliser declare -g pour déclarer des variables globales - mais dans bash 4, vous pouvez utiliser des tableaux associatifs en premier lieu, en évitant cette solution de contournement.)Sommaire:
declare -A
pour les tableaux associatifs.declare
option si vous ne pouvez pas mettre à niveau.awk
place et évitez complètement le problème.la source
4.x
et nony
.sudo port install bash
, pour ceux (à bon escient, à mon humble avis) qui ne veulent pas que les répertoires du PATH pour tous les utilisateurs soient accessibles en écriture sans escalade explicite des privilèges par processus.Il y a une substitution de paramètres, bien qu'il puisse aussi être non-PC ... comme l'indirection.
La méthode BASH 4 est bien sûr meilleure, mais si vous avez besoin d'un hack ... seul un hack fera l'affaire. Vous pouvez rechercher le tableau / hachage avec des techniques similaires.
la source
VALUE=${animal#*:}
protéger le cas oùARRAY[$x]="caesar:come:see:conquer"
for animal in "${ARRAY[@]}"; do
Voici ce que je cherchais ici:
Cela n'a pas fonctionné pour moi avec bash 4.1.5:
la source
Vous pouvez encore modifier l'interface hput () / hget () afin que vous ayez nommé des hachages comme suit:
et alors
Cela vous permet de définir d'autres cartes qui n'entrent pas en conflit (par exemple, «rcapitals» qui recherche les pays par capitale). Mais, de toute façon, je pense que vous constaterez que tout cela est assez terrible, en termes de performances.
Si vous voulez vraiment une recherche rapide de hachage, il y a un piratage terrible, terrible qui fonctionne vraiment très bien. C'est ceci: écrivez vos clés / valeurs dans un fichier temporaire, une par ligne, puis utilisez 'grep "^ $ key"' pour les extraire, en utilisant des tuyaux avec cut ou awk ou sed ou quoi que ce soit pour récupérer les valeurs.
Comme je l'ai dit, cela semble terrible, et il semble qu'il devrait être lent et faire toutes sortes d'E / S inutiles, mais en pratique, il est très rapide (le cache disque est génial, n'est-ce pas?), Même pour un hachage très volumineux les tables. Vous devez appliquer vous-même l'unicité des clés, etc. Même si vous ne disposez que de quelques centaines d'entrées, la combinaison fichier de sortie / grep sera beaucoup plus rapide - selon mon expérience, plusieurs fois plus rapide. Il mange également moins de mémoire.
Voici une façon de procéder:
la source
Utilisez simplement le système de fichiers
Le système de fichiers est une structure arborescente qui peut être utilisée comme une carte de hachage. Votre table de hachage sera un répertoire temporaire, vos clés seront des noms de fichiers et vos valeurs seront le contenu du fichier. L'avantage est qu'il peut gérer d'énormes hashmaps et ne nécessite pas de shell spécifique.
Création de table de hachage
hashtable=$(mktemp -d)
Ajouter un élément
echo $value > $hashtable/$key
Lire un élément
value=$(< $hashtable/$key)
Performance
Bien sûr, c'est lent, mais pas si lent. Je l'ai testé sur ma machine, avec un SSD et btrfs , et il fait environ 3000 éléments en lecture / écriture par seconde .
la source
mkdir -d
? (Pas 4.3, sur Ubuntu 14. J'aurais recours àmkdir /run/shm/foo
, ou si cela remplissait la RAMmkdir /tmp/foo
mktemp -d
était-ce à la place?$value=$(< $hashtable/$key)
etvalue=$(< $hashtable/$key)
? Merci!la source
${var#start}
supprime le début du texte depuis le début de la valeur stockée dans la variable var .Considérez une solution utilisant la lecture intégrée bash comme illustré dans l'extrait de code d'un script de pare-feu ufw qui suit. Cette approche présente l'avantage d'utiliser autant d'ensembles de champs délimités (et pas seulement 2) que souhaité. Nous avons utilisé le | délimiteur car les spécificateurs de plage de ports peuvent nécessiter deux-points, par exemple 6001: 6010 .
la source
IFS=$'|' read -r first rest <<< "$fields"
Je suis d'accord avec @lhunath et d'autres que le tableau associatif est la voie à suivre avec Bash 4. Si vous êtes bloqué sur Bash 3 (OSX, anciennes distributions que vous ne pouvez pas mettre à jour), vous pouvez également utiliser expr, qui devrait être partout, une chaîne et les expressions régulières. Je l'aime surtout quand le dictionnaire n'est pas trop gros.
Écrivez votre carte sous forme de chaîne (notez le séparateur «,» également au début et à la fin)
Utilisez une expression régulière pour extraire les valeurs
Fractionnez la chaîne pour répertorier les éléments
Vous pouvez maintenant l'utiliser:
la source
J'ai vraiment aimé la réponse d'Al P, mais je voulais que l'unicité soit appliquée à moindre coût, alors je suis allé plus loin: utilisez un répertoire. Il existe des limitations évidentes (limites de fichiers de répertoires, noms de fichiers invalides) mais cela devrait fonctionner dans la plupart des cas.
Il fonctionne également un peu mieux dans mes tests.
Je pensais juste me lancer. A bientôt!
Edit: Ajout de hdestroy ()
la source
Deux choses, vous pouvez utiliser la mémoire au lieu de / tmp dans n'importe quel noyau 2.6 en utilisant / dev / shm (Redhat), d'autres distributions peuvent varier. Hget peut également être réimplémenté en utilisant la lecture suivante:
De plus, en supposant que toutes les clés sont uniques, le retour court-circuite la boucle de lecture et évite d'avoir à lire toutes les entrées. Si votre implémentation peut avoir des clés en double, laissez simplement le retour. Cela économise les frais de lecture et de bifurcation grep et awk. L'utilisation de / dev / shm pour les deux implémentations a donné les résultats suivants en utilisant time hget sur un hachage à 3 entrées recherchant la dernière entrée:
Grep / Awk:
Lecture / écho:
sur de multiples invocations, je n'ai jamais vu moins qu'une amélioration de 50%. Tout cela peut être attribué à la fourche au-dessus de la tête, en raison de l'utilisation de
/dev/shm
.la source
Un collègue vient de mentionner ce fil. J'ai indépendamment implémenté des tables de hachage dans bash, et cela ne dépend pas de la version 4. D'après un de mes articles de blog en mars 2010 (avant certaines des réponses ici ...) intitulé Tables de hachage dans bash :
J'avais auparavant l' habitude
cksum
de hacher, mais j'ai depuis traduit la chaîne hashCode de Java en bash / zsh natif.Ce n'est pas bidirectionnel, et la méthode intégrée est bien meilleure, mais aucune ne devrait vraiment être utilisée de toute façon. Bash est pour des ponctuels rapides, et de telles choses devraient très rarement impliquer une complexité qui pourrait nécessiter des hachages, sauf peut-être chez vous
~/.bashrc
et vos amis.la source
Avant bash 4, il n'y a pas de bon moyen d'utiliser des tableaux associatifs dans bash. Votre meilleur pari est d'utiliser un langage interprété qui prend en charge de telles choses, comme awk. D'un autre côté, bash 4 les prend en charge.
En ce qui concerne les moins bons moyens dans bash 3, voici une référence qui pourrait aider: http://mywiki.wooledge.org/BashFAQ/006
la source
Solution Bash 3:
En lisant certaines des réponses, j'ai rassemblé une petite fonction rapide que j'aimerais apporter en retour et qui pourrait aider les autres.
la source
J'ai également utilisé la méthode bash4 mais je trouve un bug ennuyeux.
J'avais besoin de mettre à jour dynamiquement le contenu du tableau associatif, j'ai donc utilisé cette méthode:
Je découvre qu'avec bash 4.3.11, l'ajout à une clé existante dans le dict a entraîné l'ajout de la valeur si elle est déjà présente. Ainsi, par exemple, après une répétition, le contenu de la valeur était "checkKOcheckKOallCheckOK" et ce n'était pas bon.
Pas de problème avec bash 4.3.39 où ajouter une clé existante signifie sous-estimer la valeur actuale si elle est déjà présente.
J'ai résolu cela en nettoyant / déclarant le tableau associatif statusCheck avant le cicle:
la source
Je crée des HashMaps en bash 3 en utilisant des variables dynamiques. J'ai expliqué comment cela fonctionne dans ma réponse à: Tableaux associatifs dans les scripts Shell
Vous pouvez également jeter un œil à shell_map , qui est une implémentation HashMap réalisée dans bash 3.
la source