Comment définir les tables de hachage en Bash?
Ce qui est l'équivalent de Python dictionnaires mais en Bash (devrait fonctionner à travers OS X et Linux).
- Ont bash exécuter un python/perl script... C'est tellement souples!
- Pensez à utiliser xonsh (il est sur github).
Vous devez vous connecter pour publier un commentaire.
Bash 4
Bash 4 prend en charge cette fonctionnalité. Assurez-vous que votre script hashbang est
#!/usr/bin/env bash
ou#!/bin/bash
de sorte que vous ne finissent pas à l'aide desh
. Assurez-vous que vous êtes soit de l'exécution de votre script directement, ou à exécuter desscript
avecbash script
. (Et non pas de l'exécution d'un script Bash avec Bash ne arriver, et sera vraiment à confusion!)Vous déclarez un tableau associatif en faisant:
Vous pouvez le remplir avec des éléments à l'aide du tableau normal opérateur d'affectation. Par exemple, si vous voulez avoir une carte de
animal[sound(key)] = animal(value)
:Ou les fusionner:
Ensuite les utiliser comme des tableaux normaux. Utilisation
animals['key']='value'
la valeur définie,"${animals[@]}"
pour développer les valeurs, et"${!animals[@]}"
(avis de l'!
) pour développer les touches. N'oubliez pas de les citer:Bash 3
Avant bash 4, vous n'avez pas de tableaux associatifs. Ne pas utiliser
eval
à les imiter. Évitereval
comme la peste, parce qu'il est la peste de scripts shell. La raison la plus importante est queeval
traite vos données sous forme de code (il y en a beaucoup d'autres raisons aussi).D'abord et avant tout: pensez à mettre à bash 4. Cela rendra le processus beaucoup plus facile pour vous.
Si il y a une raison pour laquelle vous ne pouvez pas mettre à niveau,
declare
est de loin la meilleure option. Il n'a pas d'évaluer les données que bash-code commeeval
n', et comme tel ne peut permettre à un code arbitraire de l'injection assez facilement.Nous allons préparer la réponse en introduisant les concepts:
Tout d'abord, l'indirection.
Deuxièmement,
declare
:Les réunir:
Nous allons utiliser:
Remarque:
declare
ne peut pas être mis en fonction. Toute utilisation dedeclare
à l'intérieur d'une fonction bash tourne la variable elle crée local à la portée de cette fonction, ce qui signifie que nous ne pouvons pas accéder ou de modifier les tableaux globaux avec elle. (En bash 4 vous pouvez utiliser déclarer g de déclarer des variables globales - mais en bash 4, vous pouvez utiliser des tableaux associatifs en premier lieu, en évitant que cette solution de contournement.)Résumé:
declare -A
pour les tableaux associatifs.declare
option si vous ne pouvez pas mettre à niveau.awk
la place et éviter le problème complètement.declare -A
, je ne peux pas croire que je n'ai jamais utilisé avant! J'ai programmé bash pour 10 ans.declare -A
se plaint-A
n'est pas une option valide... des idées pourquoi? linux distr est SUSE..4.x
et pasy
.0 - dog
quand j'essaye de le votre de boucle à l'aide de bash 4.3.30 sur ubuntu 14.10${!i}
indirection fait le tour et c'est assez. Flammes de la guerre dans 3, 2, 1...$ /bin/bash --version
retourneGNU bash, version 3.2.53(1)-release (x86_64-apple-darwin12)
. Je suis toujours sur OS X 10.8.5 en raison des exigences opérationnelles.brew install bash
brew.shsudo port install bash
, pour ceux (à bon escient, à mon humble avis) ne souhaitent pas prendre des répertoires dans le CHEMIN d'accès pour tous les utilisateurs inscriptible, sans autorisation explicite et par processus de remontée.for sound in "${!animals[@]}"
"moo"
, la clé de l'entrée dans le tableau qui a la valeur"cow"
.animals_meaw=cat animals_woof=dog animals_moo=cow
vous pouvez itérer avecfor animal in ${!animals_*}; do echo "the ${!animal} goes ${animal#animals_}"; done
sorties:the cat goes meaw...
declare -A animals=([meaw]=cat [woof]=dog [moo]=cow)
vous pouvez itérer avecfor sound in ${!animals[*]}; do echo "the ${animals[$sound]} goes ${sound}"; done
sorties:the cow goes moo...
Il y a le paramètre de substitution, même si elle peut être non-PC ...comme d'indirection.
Le BASH 4 way est mieux, bien sûr, mais si vous avez besoin d'un hack ...seulement un hack va faire.
Vous pouvez rechercher le tableau/hachage avec des techniques similaires.
VALUE=${animal#*:}
pour protéger le cas oùARRAY[$x]="caesar:come:see:conquer"
for animal in "${ARRAY[@]}"; do
words=(${line})
et puis je peux choisir chaque paramètre de la matricewords
C'est ce que je cherche ici:
Cela n'a pas fonctionné pour moi avec bash 4.1.5:
Vous pouvez encore modifier les hput()/hget() de l'interface de sorte que vous avez nommé les hachages comme suit:
et puis
Cela vous permet de définir d'autres cartes qui ne sont pas en conflit (par exemple, 'rcapitals" qui n'pays recherche par ville capitale). Mais, de toute façon, je pense que vous trouverez que c'est assez terrible, en terme de performance.
Si vous voulez vraiment rapide hachage de recherche, il y a un terrible, terrible hack qui fonctionne vraiment bien. C'est cela: écrire vos clés/valeurs dans un fichier temporaire, un par ligne, puis utiliser " grep "^$touche" " pour les faire sortir à l'aide de tubes à couper ou awk ou sed ou que ce soit pour récupérer les valeurs.
Comme je l'ai dit, il a l'air terrible, et il semble que ça devrait être lente et de faire toutes sortes de inutiles IO, mais dans la pratique, il est très rapide (cache disque est génial, n'est-ce pas?), même pour les très grandes tables de hachage. Vous devez appliquer la clé de l'unicité de vous-même, etc. Même si vous n'avez que quelques centaines d'entrées, le fichier de sortie/grep combo va être un peu plus rapide - en mon expérience plusieurs fois plus rapide. Elle consomme également moins de mémoire.
Voici une façon de le faire:
Suffit d'utiliser le système de fichiers
Le système de fichiers est une structure arborescente qui peut être utilisé comme une table de hachage de la carte.
Votre table de hachage sera un répertoire temporaire, vos clés seront les noms de fichiers et de vos valeurs seront le contenu du fichier. L'avantage est qu'il peut gérer d'énormes hashmaps, et n'a pas besoin d'un shell.
Hashtable création
hashtable=$(mktemp -d)
Ajouter un élément
echo $value > $hashtable/$key
Lire un élément
value=$(< $hashtable/$key)
Performance
Bien sûr, de son lent, mais pas que lent.
Je l'ai testé sur ma machine, avec un SSD et btrfs, et il n'a autour de 3000 élément en lecture/écriture par seconde.
mkdir -d
? (Pas de 4,3, sur Ubuntu 14. J'avais recours àmkdir /run/shm/foo
, ou si rempli de la RAM,mkdir /tmp/foo
.)mktemp -d
a été signifié à la place?$value=$(< $hashtable/$key)
etvalue=$(< $hashtable/$key)
? Merci!Envisager une solution en utilisant le bash builtin lire comme illustré dans l'extrait de code à partir d'un ufw script de pare-feu qui suit. Cette approche a l'avantage de l'utilisation que de nombreux délimité le terrain de jeux (et pas seulement 2) comme souhaité. Nous avons utilisé le | délimiteur car le port de la gamme des prescripteurs peuvent nécessiter un colon, c'est à dire 6001:6010.
IFS=$'|' read -r first rest <<< "$fields"
Je suis d'accord avec @lhunath et d'autres personnes que le tableau associatif sont le chemin à parcourir avec Bash 4. Si vous êtes coincé à Bash 3 (OSX, ancien distributions que vous ne pouvez pas mettre à jour), vous pouvez utiliser aussi expr, qui doit être partout, une chaîne de caractères et expressions régulières. Je l'aime surtout quand le dictionnaire n'est pas trop grand.
Écrire votre carte comme une chaîne de caractères (notez le séparateur", " aussi au début et à la fin)
Utiliser une expression régulière pour extraire les valeurs
Fractionner la chaîne à la liste des articles
Maintenant, vous pouvez l'utiliser:
J'ai vraiment aimé Al P réponse mais je voulais unicité appliquée à bon marché donc j'ai pris un peu plus loin - l'utilisation d'un répertoire. Il y a quelques limitations évidentes (fichier de l'annuaire des limites, des noms de fichier non valides), mais il devrait fonctionner pour la plupart des cas.
Il effectue également un peu mieux dans mes tests.
Juste pensé que je le relève. Cheers!
Edit: Ajout de hdestroy()
Avant bash 4 il n'y a pas de bonne façon d'utiliser des tableaux associatifs dans bash. Votre meilleur pari est d'utiliser un langage interprété qui a réellement de soutien pour de telles choses, comme awk. D'autre part, bash 4 ne les soutenir.
Comme pour moins bonnes façons de bash 3, ici est une référence que l'on pourrait aider: http://mywiki.wooledge.org/BashFAQ/006
Deux choses, vous pouvez utiliser de la mémoire au lieu de /tmp dans tout le noyau 2.6 en utilisant /dev/shm (Redhat) autres distributions peuvent varier. Aussi hget peut être réimplémentée à utiliser la lecture comme suit:
En outre, en supposant que toutes les clés sont uniques, le retour des courts-circuits, la lecture en boucle et évite d'avoir à lire toutes les entrées. Si votre application peut avoir des doubles de clés, puis il suffit de laisser le bon de retour. Cela permet d'économiser la charge de la lecture et de la fourche à la fois grep et awk. L'utilisation de /dev/shm pour les deux implémentations ont donné les résultats suivants à l'aide de temps hget sur un 3 entrée de hachage pour la recherche de la dernière entrée :
Grep/Awk:
Lire/echo:
sur de multiples appels je n'ai jamais vu à moins d'une amélioration de 50%.
Tout cela peut être attribuée à la fourchette dessus de la tête, en raison de l'utilisation de
/dev/shm
.Bash 3 solution:
Dans la lecture de certaines réponses j'ai mis en place rapidement une petite fonction que j'aimerais contribuer en retour qui pourrait aider les autres.
Un collègue vient de le mentionner sur ce thread. J'ai mis en œuvre les tables de hachage à l'intérieur de bash, et il ne dépend pas de la version 4. D'un billet de blog de la mine en Mars 2010 (avant certaines des réponses ici...) intitulé Les tables de hachage en bash:
Je auparavant utilisé
cksum
de hachage, mais ont depuis traduit Java de la chaîne hashCode natif bash/zsh.Ce n'est pas bidirectionnelle, et le haut-moyen est beaucoup mieux, mais ne devrait vraiment être utilisé de toute façon. Bash est rapide one-offs, et de telles choses devraient très rarement impliquent de la complexité qui peut nécessiter de hachages, sauf peut-être dans votre
~/.bashrc
et amis.J'ai aussi utilisé la bash4 façon mais j'ai trouver et bug gênant.
J'avais besoin de mettre à jour dynamiquement le tableau associatif contenu j'ai donc utilisé de cette façon:
Je trouve qu'avec bash 4.3.11 ajout d'une clé existante dans le dict a entraîné l'ajout de la valeur, s'il est déjà présent. Ainsi, par exemple, après quelques repetion le contenu de la valeur a été "checkKOcheckKOallCheckOK" et ce n'était pas bon.
Pas de problème avec bash 4.3.39 où appenging un existant moyen clé pour substisture la actuale valeur si elle existe déjà.
J'ai résolu ce problème il suffit de nettoyer/déclarer la statusCheck tableau associatif avant de la cicle:
Je créer HashMaps en bash 3 à l'aide de variables dynamiques. J'ai expliqué comment cela fonctionne dans ma réponse à: Tableaux associatifs dans les scripts Shell
Aussi, vous pouvez prendre un coup d'oeil dans shell_map, qui est une table de hachage mise en œuvre fait en bash 3.