Nous avions besoin d'un script qui simule des tableaux associatifs ou une structure de données de type Map pour Shell Scripting, n'importe quel corps?
bash
shell
hashtable
associative-array
Irfan Zulfiqar
la source
la source
Une autre option, si la portabilité n'est pas votre principale préoccupation, consiste à utiliser des tableaux associatifs intégrés au shell. Cela devrait fonctionner dans bash 4.0 (disponible maintenant sur la plupart des distributions majeures, mais pas sur OS X sauf si vous l'installez vous-même), ksh et zsh:
Selon le shell, vous devrez peut-être faire un à la
typeset -A newmap
place dedeclare -A newmap
, ou dans certains, cela peut ne pas être nécessaire du tout.la source
test -z ${variable+x}
(celax
n'a pas d'importance, cela pourrait être n'importe quelle chaîne). Pour un tableau associatif dans Bash, vous pouvez faire la même chose; utilisertest -z ${map[key]+x}
.Une autre façon non-bash 4.
Vous pouvez également lancer une instruction if pour effectuer une recherche. si [[$ var = ~ / blah /]]. ou peu importe.
la source
Je pense que vous devez prendre du recul et réfléchir à ce qu'est vraiment une carte ou un tableau associatif. Il s'agit simplement d'un moyen de stocker une valeur pour une clé donnée et de récupérer cette valeur rapidement et efficacement. Vous souhaiterez peut-être également pouvoir parcourir les clés pour récupérer chaque paire clé / valeur ou supprimer des clés et leurs valeurs associées.
Maintenant, pensez à une structure de données que vous utilisez tout le temps dans les scripts shell, et même juste dans le shell sans écrire un script, qui possède ces propriétés. Perplexe? C'est le système de fichiers.
En réalité, tout ce dont vous avez besoin pour avoir un tableau associatif dans la programmation shell est un répertoire temporaire.
mktemp -d
est votre constructeur de tableau associatif:Si vous n'avez pas envie d'utiliser
echo
etcat
, vous pouvez toujours écrire de petits wrappers; ceux-ci sont modélisés à partir de ceux d'Irfan, bien qu'ils ne produisent que la valeur plutôt que de définir des variables arbitraires comme$value
:edit : Cette approche est en fait un peu plus rapide que la recherche linéaire utilisant sed suggérée par le questionneur, ainsi que plus robuste (elle permet aux clés et valeurs de contenir -, =, espace, qnd ": SP:"). Le fait qu'il utilise le système de fichiers ne le ralentit pas; l'écriture sur le disque de ces fichiers n'est en fait jamais garantie à moins que vous n'appeliez
sync
; pour des fichiers temporaires comme celui-ci avec une durée de vie courte, il n'est pas improbable que beaucoup d'entre eux ne soient jamais écrits sur le disque.J'ai fait quelques benchmarks du code d'Irfan, de la modification par Jerry du code d'Irfan et de mon code, en utilisant le programme pilote suivant:
Les resultats:
la source
Bash4 prend en charge cela de manière native. N'utilisez pas
grep
oueval
, ce sont les plus laids des hacks.Pour une réponse détaillée et détaillée avec un exemple de code, voir: /programming/3467959
la source
Exemple:
la source
Répondant maintenant à cette question.
Les scripts suivants simulent des tableaux associatifs dans des scripts shell. C'est simple et très facile à comprendre.
La carte n'est rien d'autre qu'une chaîne sans fin qui a keyValuePair enregistré sous --name = Irfan --designation = SSE --company = My: SP: Own: SP: Company
les espaces sont remplacés par «: SP:» pour les valeurs
edit: vient d'ajouter une autre méthode pour récupérer toutes les clés.
la source
eval
des données comme si c'était du code bash, et en plus: vous ne les citez pas correctement. Les deux provoquent une masse de bogues et une injection de code arbitraire.Pour Bash 3, il existe un cas particulier qui a une solution simple et agréable:
Si vous ne voulez pas gérer beaucoup de variables, ou si les clés sont simplement des identificateurs de variables non valides et que votre tableau est garanti d'avoir moins de 256 éléments , vous pouvez abuser des valeurs de retour de fonction. Cette solution ne nécessite aucun sous-shell car la valeur est facilement disponible en tant que variable, ni aucune itération pour que les performances crient. De plus, il est très lisible, presque comme la version Bash 4.
Voici la version la plus basique:
N'oubliez pas, utilisez des guillemets simples dans
case
, sinon il est sujet à un globbing. Vraiment utile pour les hachages statiques / gelés dès le début, mais on pourrait écrire un générateur d'index à partir d'unhash_keys=()
tableau.Attention, il est par défaut le premier, donc vous voudrez peut-être mettre de côté le zéro élément:
Attention: la longueur est désormais incorrecte.
Sinon, si vous souhaitez conserver l'indexation de base zéro, vous pouvez réserver une autre valeur d'index et vous protéger contre une clé inexistante, mais elle est moins lisible:
Ou, pour garder la longueur correcte, offset de un:
la source
Vous pouvez utiliser des noms de variables dynamiques et laisser les noms de variables fonctionner comme les clés d'un hashmap.
Par exemple, si vous avez un fichier d'entrée avec deux colonnes, nom, crédit, comme dans l'exemple ci-dessous, et que vous souhaitez additionner les revenus de chaque utilisateur:
La commande ci-dessous résumera tout, en utilisant des variables dynamiques comme clés, sous la forme de map _ $ {person} :
Pour lire les résultats:
La sortie sera:
En développant ces techniques, je développe sur GitHub une fonction qui fonctionne exactement comme un objet HashMap , shell_map .
Afin de créer des " instances HashMap ", la fonction shell_map est capable de créer des copies d'elle-même sous différents noms. Chaque nouvelle copie de fonction aura une variable $ FUNCNAME différente. $ FUNCNAME est alors utilisé pour créer un espace de noms pour chaque instance de Map.
Les clés de la carte sont des variables globales, sous la forme $ FUNCNAME_DATA_ $ KEY, où $ KEY est la clé ajoutée à la carte. Ces variables sont des variables dynamiques .
Ci-dessous, je vais mettre une version simplifiée de celui-ci afin que vous puissiez l'utiliser comme exemple.
Usage:
la source
Encore une autre manière non-bash-4 (c'est-à-dire, bash 3, compatible Mac):
Impressions:
La fonction avec les
case
agit comme un tableau associatif. Malheureusement, il ne peut pas utiliserreturn
, donc il doit àecho
sa sortie, mais ce n'est pas un problème, à moins que vous ne soyez un puriste qui évite de forger des sous-shell.la source
Quel dommage que je n'ai pas vu la question avant - j'ai écrit une bibliothèque shell-framework qui contient entre autres les cartes (tableaux associatifs). La dernière version de celui-ci peut être trouvée ici .
Exemple:
la source
Ajout d'une autre option, si jq est disponible:
la source
J'ai trouvé vrai, comme déjà mentionné, que la méthode la plus performante consiste à écrire des clés / vals dans un fichier, puis à utiliser grep / awk pour les récupérer. Cela ressemble à toutes sortes d'E / S inutiles, mais le cache disque intervient et le rend extrêmement efficace - beaucoup plus rapide que d'essayer de les stocker en mémoire en utilisant l'une des méthodes ci-dessus (comme le montrent les benchmarks).
Voici une méthode rapide et propre que j'aime:
Si vous souhaitez appliquer une valeur unique par clé, vous pouvez également faire une petite action grep / sed dans hput ().
la source
il y a plusieurs années, j'ai écrit une bibliothèque de scripts pour bash qui supportait les tableaux associatifs entre autres fonctionnalités (journalisation, fichiers de configuration, prise en charge étendue de l'argument de ligne de commande, génération d'aide, tests unitaires, etc.). La bibliothèque contient un wrapper pour les tableaux associatifs et bascule automatiquement vers le modèle approprié (interne pour bash4 et émuler pour les versions précédentes). Il s'appelait shell-framework et était hébergé sur origo.ethz.ch mais aujourd'hui, la ressource est fermée. Si quelqu'un en a encore besoin, je peux le partager avec vous.
la source
Shell n'a pas de carte intégrée comme la structure de données, j'utilise une chaîne brute pour décrire des éléments comme ça:
lors de l'extraction d'éléments et de ses attributs:
Cela ne semble pas intelligent que la réponse des autres, mais facile à comprendre pour les nouvelles personnes.
la source
J'ai modifié la solution de Vadim avec ce qui suit:
Le changement est de map_get afin de l'empêcher de renvoyer des erreurs si vous demandez une clé qui n'existe pas, bien que l'effet secondaire soit qu'il ignorera également silencieusement les cartes manquantes, mais cela convenait mieux à mon cas d'utilisation puisque je viens de voulait vérifier une clé afin de sauter des éléments dans une boucle.
la source
Réponse tardive, mais envisagez de résoudre le problème de cette manière, en 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 a l'avantage d'utiliser autant d'ensembles de champs délimités (pas seulement 2) que vous le souhaitez. Nous avons utilisé le | délimiteur car les spécificateurs de plage de ports peuvent nécessiter deux points, c'est -à- dire 6001: 6010 .
la source