Exigimos um script que simula matrizes associativas ou estrutura de dados como um mapa para Shell Scripting, qualquer corpo?
bash
shell
hashtable
associative-array
Irfan Zulfiqar
fonte
fonte
Outra opção, se a portabilidade não for sua preocupação principal, é usar matrizes associativas que são integradas ao shell. Isso deve funcionar no bash 4.0 (disponível agora na maioria das principais distros, embora não no OS X, a menos que você mesmo instale), ksh e zsh:
Dependendo do shell, você pode precisar fazer um em
typeset -A newmap
vez dedeclare -A newmap
, ou em alguns pode nem ser necessário.fonte
test -z ${variable+x}
(x
não importa, pode ser qualquer string). Para uma matriz associativa no Bash, você pode fazer o mesmo; usartest -z ${map[key]+x}
.Outra forma não-bash 4.
Você também pode lançar uma instrução if para pesquisar lá. if [[$ var = ~ / blah /]]. como queiras.
fonte
Eu acho que você precisa dar um passo atrás e pensar sobre o que um mapa, ou array associativo, realmente é. Tudo isso é uma maneira de armazenar um valor para uma determinada chave e obter esse valor de volta com rapidez e eficiência. Você também pode querer iterar as chaves para recuperar cada par de valores-chave ou excluir chaves e seus valores associados.
Agora, pense em uma estrutura de dados que você usa o tempo todo em scripts de shell, e mesmo apenas no shell sem escrever um script, que tenha essas propriedades. Perplexo? É o sistema de arquivos.
Na verdade, tudo o que você precisa para ter uma matriz associativa na programação do shell é um diretório temporário.
mktemp -d
é o seu construtor de matriz associativa:Se você não quiser usar
echo
ecat
, você pode escrever alguns pequenos invólucros; estes são modelados a partir do Irfan, embora eles apenas gerem o valor em vez de definir variáveis arbitrárias como$value
:editar : Esta abordagem é na verdade um pouco mais rápida do que a pesquisa linear usando sed sugerida pelo questionador, bem como mais robusta (permite que chaves e valores contenham -, =, espaço, qnd ": SP:"). O fato de usar o sistema de arquivos não o torna lento; esses arquivos nunca têm garantia de serem gravados no disco, a menos que você chame
sync
; para arquivos temporários como esse com uma vida útil curta, não é improvável que muitos deles nunca sejam gravados no disco.Fiz alguns benchmarks do código de Irfan, a modificação de Jerry do código de Irfan e meu código, usando o seguinte programa de driver:
Os resultados:
fonte
Bash4 oferece suporte nativo. Não use
grep
oueval
, eles são os mais feios dos hacks.Para uma resposta detalhada e detalhada com código de exemplo, consulte: /programming/3467959
fonte
Exemplo:
fonte
Agora respondendo a esta pergunta.
Os scripts a seguir simulam arrays associativos em scripts de shell. É simples e muito fácil de entender.
Mapa nada mais é do que uma string sem fim que tem keyValuePair salvo como --name = Irfan --designation = SSE --company = Meu: SP: Próprio: SP: Empresa
espaços são substituídos por ': SP:' para valores
editar: acabou de adicionar outro método para buscar todas as chaves.
fonte
eval
inserindo dados como se fossem código bash, e mais: você falha ao citá-los corretamente. Ambos causam muitos bugs e injeção arbitrária de código.Para o Bash 3, há um caso particular que tem uma solução simples e agradável:
Se você não quiser lidar com muitas variáveis ou se as chaves forem simplesmente identificadores de variáveis inválidos, e se sua matriz tiver a garantia de ter menos de 256 itens , você pode abusar dos valores de retorno da função. Esta solução não requer nenhum subshell, pois o valor está prontamente disponível como uma variável, nem qualquer iteração de modo que o desempenho grite. Também é muito legível, quase como a versão Bash 4.
Esta é a versão mais básica:
Lembre-se, use aspas simples
case
, caso contrário, está sujeito a globbing. Muito útil para hashes estáticos / congelados desde o início, mas pode-se escrever um gerador de índice a partir de umhash_keys=()
array.Cuidado, o padrão é o primeiro, então você pode querer deixar de lado o elemento zero:
Advertência: o comprimento agora está incorreto.
Como alternativa, se você deseja manter a indexação baseada em zero, pode reservar outro valor de índice e se proteger contra uma chave inexistente, mas é menos legível:
Ou, para manter o comprimento correto, desloque o índice em um:
fonte
Você pode usar nomes de variáveis dinâmicas e permitir que os nomes das variáveis funcionem como as chaves de um hashmap.
Por exemplo, se você tem um arquivo de entrada com duas colunas, nome, crédito, como no exemplo abaixo, e deseja somar a renda de cada usuário:
O comando abaixo somará tudo, usando variáveis dinâmicas como chaves, na forma de mapa _ $ {pessoa} :
Para ler os resultados:
O resultado será:
Elaborando essas técnicas, estou desenvolvendo no GitHub uma função que funciona exatamente como um objeto HashMap , shell_map .
Para criar " instâncias de HashMap ", a função shell_map é capaz de criar cópias de si mesma com nomes diferentes. Cada nova cópia de função terá uma variável $ FUNCNAME diferente. $ FUNCNAME então é usado para criar um namespace para cada instância do Mapa.
As chaves do mapa são variáveis globais, no formato $ FUNCNAME_DATA_ $ KEY, onde $ KEY é a chave adicionada ao Mapa. Essas variáveis são variáveis dinâmicas .
Abaixo colocarei uma versão simplificada para que você possa usar como exemplo.
Uso:
fonte
Ainda outra forma não-bash-4 (ou seja, bash 3, compatível com Mac):
Impressões:
A função com o
case
atua como uma matriz associativa. Infelizmente, ele não pode usarreturn
, por isso tem deecho
produzir, mas isso não é um problema, a menos que você seja um purista que evita bifurcar subshells.fonte
É uma pena que não tenha visto a pergunta antes - escrevi biblioteca shell-framework que contém, entre outros, os mapas (matrizes associativas). A última versão pode ser encontrada aqui .
Exemplo:
fonte
Adicionar outra opção, se jq estiver disponível:
fonte
Descobri que é verdade, como já mencionei, que o método de melhor desempenho é gravar key / vals em um arquivo e, em seguida, usar grep / awk para recuperá-los. Parece todo tipo de IO desnecessário, mas o cache de disco entra em ação e o torna extremamente eficiente - muito mais rápido do que tentar armazená-los na memória usando um dos métodos acima (como mostram os benchmarks).
Este é um método rápido e limpo de que gosto:
Se você quiser impor um valor único por chave, também pode fazer uma pequena ação grep / sed em hput ().
fonte
vários anos atrás, escrevi uma biblioteca de scripts para o bash que suportava matrizes associativas entre outros recursos (registro, arquivos de configuração, suporte estendido para argumento de linha de comando, geração de ajuda, teste de unidade, etc). A biblioteca contém um wrapper para matrizes associativas e alterna automaticamente para o modelo apropriado (interno para bash4 e emular para versões anteriores). Era chamado de shell-framework e hospedado em origo.ethz.ch, mas hoje o recurso está fechado. Se alguém ainda precisar, posso compartilhar com você.
fonte
Shell não tem um mapa embutido como estrutura de dados, eu uso string bruta para descrever itens como este:
ao extrair itens e seus atributos:
Isso não parece inteligente do que a resposta de outras pessoas, mas fácil de entender para que novas pessoas descubram.
fonte
Modifiquei a solução de Vadim com o seguinte:
A mudança é para map_get a fim de evitar que ele retorne erros se você solicitar uma chave que não existe, embora o efeito colateral seja que ele também irá ignorar silenciosamente os mapas ausentes, mas se adequou melhor ao meu caso de uso, já que eu apenas queria verificar se há uma chave para pular itens em um loop.
fonte
Resposta tardia, mas considere resolver o problema desta forma, usando o bash builtin lido conforme ilustrado no fragmento de código de um script de firewall ufw a seguir. Essa abordagem tem a vantagem de usar tantos conjuntos de campos delimitados (não apenas 2) quantos forem desejados. Usamos o | delimitador porque os especificadores de intervalo de porta podem exigir dois pontos, ou seja, 6001: 6010 .
fonte