Qual é o equivalente dos dicionários Python, exceto no Bash (deve funcionar no OS X e Linux).
bash
dictionary
hashtable
associative-array
Sridhar Ratnakumar
fonte
fonte
Respostas:
Bash 4
O Bash 4 suporta nativamente esse recurso. Verifique se o hashbang do seu script é
#!/usr/bin/env bash
ou#!/bin/bash
não você acaba usandosh
. Verifique se você está executando seu script diretamente ou executescript
combash script
. (Não realmente executar um script Bash com Bash não acontecer, e vai ser realmente confuso!)Você declara uma matriz associativa fazendo:
Você pode preenchê-lo com elementos usando o operador de atribuição de matriz normal. Por exemplo, se você deseja ter um mapa de
animal[sound(key)] = animal(value)
:Ou mescle-os:
Em seguida, use-os como matrizes normais. Usar
animals['key']='value'
definir valor"${animals[@]}"
expandir os valores"${!animals[@]}"
(observe o!
) para expandir as chavesNão se esqueça de citá-los:
Bash 3
Antes do bash 4, você não tinha matrizes associativas. Não use
eval
para emulá-los . Eviteeval
como a praga, porque é a praga do script de shell. O motivo mais importante é queeval
trata seus dados como código executável (também existem muitos outros).Primeiro e mais importante : considere atualizar para o bash 4. Isso facilitará todo o processo para você.
Se houver um motivo para não atualizar,
declare
é uma opção muito mais segura. Ele não avalia os dados como o código do bash, comoeval
faz e, como tal, não permite a injeção de código arbitrário com tanta facilidade.Vamos preparar a resposta, introduzindo os conceitos:
Primeiro, indireção.
Em segundo lugar
declare
:Junte-os:
Vamos usá-lo:
Nota:
declare
não pode ser colocado em uma função. Qualquer uso dedeclare
dentro de uma função bash transforma a variável criada localmente no escopo dessa função, o que significa que não podemos acessar ou modificar matrizes globais com ela. (No bash 4, você pode usar declare -g para declarar variáveis globais - mas no bash 4, você pode usar matrizes associativas em primeiro lugar, evitando esta solução alternativa.)Resumo:
declare -A
para matrizes associativas.declare
opção se você não pode atualizar.awk
e evite o problema completamente.fonte
4.x
e nãoy
.sudo port install bash
, para aqueles (sabiamente, IMHO) que não desejam criar diretórios no PATH para todos os usuários graváveis sem escalação explícita de privilégios por processo.Há substituição de parâmetro, embora também possa ser não-PC ... como indireto.
O caminho do BASH 4 é melhor, é claro, mas se você precisar de um hack ... apenas um hack será suficiente. Você pode pesquisar o array / hash com técnicas semelhantes.
fonte
VALUE=${animal#*:}
proteger o casoARRAY[$x]="caesar:come:see:conquer"
for animal in "${ARRAY[@]}"; do
Isto é o que eu estava procurando aqui:
Isso não funcionou para mim com o bash 4.1.5:
fonte
Você pode modificar ainda mais a interface hput () / hget () para nomear hashes da seguinte maneira:
e depois
Isso permite que você defina outros mapas que não conflitem (por exemplo, 'rcapitals', que pesquisam o país pela capital). Mas, de qualquer forma, acho que você descobrirá que tudo isso é terrível, em termos de desempenho.
Se você realmente deseja uma pesquisa rápida por hash, há um hack terrível que realmente funciona muito bem. É isso: escreva seus valores-chave em um arquivo temporário, um por linha, e use 'grep "^ $ key"' para obtê-los, usando tubos com cut ou awk ou sed ou o que quer que seja para recuperar os valores.
Como eu disse, parece terrível, e parece que deve ser lento e fazer todo tipo de IO desnecessário, mas na prática é muito rápido (o cache do disco é incrível, não é?), Mesmo para hash muito grande tabelas. Você mesmo deve aplicar a exclusividade da chave, etc. Mesmo se você tiver apenas algumas centenas de entradas, o arquivo de saída / combinação grep será um pouco mais rápido - na minha experiência, várias vezes mais rápido. Também consome menos memória.
Aqui está uma maneira de fazer isso:
fonte
Basta usar o sistema de arquivos
O sistema de arquivos é uma estrutura em árvore que pode ser usada como um mapa de hash. Sua tabela de hash será um diretório temporário, suas chaves serão nomes de arquivos e seus valores serão o conteúdo do arquivo. A vantagem é que ele pode lidar com enormes hashmaps e não requer um shell específico.
Criação de Hashtable
hashtable=$(mktemp -d)
Adicione um elemento
echo $value > $hashtable/$key
Leia um elemento
value=$(< $hashtable/$key)
atuação
Claro, é lento, mas não tão lento. Eu testei na minha máquina, com um SSD e btrfs , e faz cerca de 3000 elementos de leitura / gravação por segundo .
fonte
mkdir -d
? (Não 4.3, no Ubuntu 14. Eu recorrer amkdir /run/shm/foo
, ou se que encheu RAM,mkdir /tmp/foo
.)mktemp -d
fosse para isso?$value=$(< $hashtable/$key)
evalue=$(< $hashtable/$key)
? Obrigado!fonte
${var#start}
remove o início do texto do início do valor armazenado na variável var .Considere uma solução utilizando a festa builtin leitura como ilustrado dentro do trecho de código a partir de um script de firewall UFW que se segue. Essa abordagem tem a vantagem de usar quantos conjuntos de campos delimitados (não apenas 2) forem desejados. Nós usamos o | delimitador porque os especificadores de intervalo de portas podem exigir dois pontos, ou seja, 6001: 6010 .
fonte
IFS=$'|' read -r first rest <<< "$fields"
Concordo com @lhunath e outros que a matriz associativa é o caminho a seguir com o Bash 4. Se você está preso ao Bash 3 (OSX, distros antigos que você não pode atualizar), também pode usar o expr, que deve estar em toda parte, uma string e expressões regulares. Eu gosto especialmente quando o dicionário não é muito grande.
Escreva seu mapa como uma string (observe o separador ',' também no início e no final)
Use uma regex para extrair os valores
Divida a sequência para listar os itens
Agora você pode usá-lo:
fonte
Eu realmente gostei da resposta de Al P, mas queria que a exclusividade fosse aplicada de forma barata, então dei um passo adiante - use um diretório. Existem algumas limitações óbvias (limites de arquivo de diretório, nomes de arquivo inválidos), mas ele deve funcionar na maioria dos casos.
Ele também tem um desempenho um pouco melhor nos meus testes.
Só pensei em dar um lance. Saúde!
Edit: Adicionando hdestroy ()
fonte
Duas coisas, você pode usar memória em vez de / tmp em qualquer kernel 2.6 usando / dev / shm (Redhat). Outras distribuições podem variar. Também o hget pode ser reimplementado usando a leitura da seguinte maneira:
Além disso, assumindo que todas as teclas são únicas, o retorno causa um curto-circuito no loop de leitura e evita a leitura de todas as entradas. Se sua implementação puder ter chaves duplicadas, simplesmente deixe de fora o retorno. Isso economiza as despesas de leitura e bifurcação de grep e awk. O uso de / dev / shm para ambas as implementações produziu o seguinte usando o time hget em um hash de 3 entradas procurando a última entrada:
Grep / Awk:
Leitura / eco:
em várias invocações, nunca vi menos de 50% de melhoria. Tudo isso pode ser atribuído à sobrecarga, devido ao uso de
/dev/shm
.fonte
Um colega de trabalho acabou de mencionar esse tópico. Eu implementei tabelas de hash de maneira independente no bash e não depende da versão 4. De uma publicação minha em março de 2010 (antes de algumas das respostas aqui ...) intitulada Tabelas de hash no bash :
I anteriormente usado
cksum
para haxixe, mas desde então traduzido hashCode corda de Java para bater nativa / zsh.Não é bidirecional, e a maneira integrada é muito melhor, mas também não deve ser usada. O Bash é único, e essas coisas raramente envolvem complexidade que pode exigir hashes, exceto talvez em você
~/.bashrc
e em seus amigos.fonte
Antes do bash 4, não havia uma boa maneira de usar matrizes associativas no bash. Sua melhor aposta é usar uma linguagem interpretada que realmente suporte essas coisas, como o awk. Por outro lado, o bash 4 não apoiá-los.
Quanto às maneiras menos boas no bash 3, aqui está uma referência que pode ajudar: http://mywiki.wooledge.org/BashFAQ/006
fonte
Solução Bash 3:
Ao ler algumas das respostas, reuni uma pequena função que gostaria de contribuir de volta para ajudar outras pessoas.
fonte
Eu também usei o caminho bash4, mas acho um bug irritante.
Eu precisava atualizar dinamicamente o conteúdo do array associativo, então usei desta maneira:
Descobri que, com o bash 4.3.11 anexado a uma chave existente no dict, o resultado foi acrescentado, se já estiver presente. Por exemplo, após algumas repetições, o conteúdo do valor era "checkKOcheckKOallCheckOK" e isso não era bom.
Não há problema com o bash 4.3.39, onde aplicar uma chave existente significa subestimar o valor atual, se já estiver presente.
Eu resolvi isso apenas limpando / declarando a matriz associativa statusCheck antes do ciclo:
fonte
Eu crio HashMaps no bash 3 usando variáveis dinâmicas. Expliquei como isso funciona na minha resposta a: Matrizes associativas em scripts Shell
Além disso, você pode dar uma olhada no shell_map , que é uma implementação do HashMap feita no bash 3.
fonte