Qual é o objetivo do comando hash?

118

Se você executar, hashele mostra o caminho de todos os comandos executados desde a última redefinição do hash ( hash -r)

[root@c04c ~]# hash
hash: hash table empty

[root@c04c ~]# whoami
root

[root@c04c ~]# hash
hits    command
   1    /usr/bin/whoami

[root@c04c ~]# whoami
root

[root@c04c ~]# hash
hits    command
   2    /usr/bin/whoami

De acordo com as páginas de manual, o objetivo do hash é:

O utilitário / usr / bin / hash afeta a maneira como o ambiente atual do shell se lembra dos locais dos utilitários encontrados. Dependendo dos argumentos especificados, ele adiciona locais de utilitários à sua lista de locais lembrados ou elimina o conteúdo da lista. Quando nenhum argumento é especificado, ele relata o conteúdo da lista. A -ropção faz com que o shell esqueça todos os locais lembrados.

Os utilitários fornecidos como integrados ao shell não são relatados por hash.

Além de ver quantas vezes eu inseri um comando, não consigo ver o utilitário hash.

Foi ainda destaque nos 15 principais comandos úteis do thegeekstuff.com

De que maneira é hashútil?

spuder
fonte

Respostas:

97

hashé um comando interno do bash. A tabela de hash é um recurso bash que impede que seja necessário pesquisar $PATHtoda vez que você digita um comando, armazenando em cache os resultados na memória. A tabela é limpa em eventos que obviamente invalidam os resultados (como modificar $PATH)

O hashcomando é exatamente como você interage com esse sistema (por qualquer motivo que julgue necessário).

Alguns casos de uso:

  • Como você viu, imprime quantas vezes você bate em quais comandos, se você digitar sem argumentos. Isso pode lhe dizer quais comandos você usa com mais frequência.

  • Você também pode usá-lo para lembrar de executáveis ​​em locais fora do padrão.

Exemplo:

[root@policyServer ~]# hash -p /lol-wut/whoami whoami
[root@policyServer ~]# whoami
Not what you're thinking
[root@policyServer ~]# which whoami
/usr/bin/whoami
[root@policyServer ~]# /usr/bin/whoami
root
[root@policyServer ~]#

O que pode ser útil se você apenas tiver um único executável em um diretório fora do $PATHqual deseja executar, basta digitar o nome em vez de incluir tudo nesse diretório (que seria o efeito se você o incluísse $PATH).

Geralmente, um alias também pode fazer isso e, como você está modificando o comportamento do shell atual, ele não é mapeado nos programas que você inicia. Um link simbólico para o executável solitário é provavelmente a opção preferida aqui. hashé uma maneira de fazer isso.

  • Você pode usá-lo para não lembrar os caminhos dos arquivos. Isso é útil se um novo executável aparecer em um PATHdiretório anterior ou chegar mva outro lugar e você desejar forçar o bash a sair e encontrá-lo novamente, em vez do último local em que ele se lembra de encontrá-lo.

Exemplo:

[root@policyServer ~]# hash
hits    command
   1    /bin/ls
[root@policyServer ~]# cp /bin/ls /lol-wut
[root@policyServer ~]# hash
hits    command
   1    /bin/cp
   1    /bin/ls
[root@policyServer ~]# hash -d ls
[root@policyServer ~]# ls
default.ldif  newDIT.ldif  notes.txt  users.ldif
[root@policyServer ~]# hash
hits    command
   1    /bin/cp
   1    /lol-wut/ls
[root@policyServer ~]#

O cpcomando fez com que uma nova versão do lsexecutável aparecesse mais cedo no meu, $PATHmas não acionou uma limpeza da tabela de hash. Eu costumava hash -dlimpar seletivamente a entrada lsda tabela de hash. O Bash foi forçado a procurar $PATHnovamente e, quando o encontrou, encontrou-o no local mais recente (anteriormente em $ PATH do que estava sendo executado antes).

Você pode invocar seletivamente o $PATHcomportamento "encontrar novo local do executável de ", no entanto:

[root@policyServer ~]# hash
hits    command
   1    /bin/ls
[root@policyServer ~]# hash ls
[root@policyServer ~]# hash
hits    command
   0    /lol-wut/ls
[root@policyServer ~]#

Você preferiria fazer isso principalmente se quisesse algo fora da tabela de hash e não estivesse 100% de que poderia sair e depois voltar com sucesso, ou quis preservar algumas modificações feitas no shell.

Para se livrar de mapeamentos obsoletos, você também pode fazer hash -r(ou export PATH=$PATH) o que efetivamente limpa a tabela de hash inteira do bash.

Existem muitas pequenas situações como essa. Não sei se o chamaria de um dos comandos "mais úteis", mas ele tem alguns casos de uso.

Bratchley
fonte
Eu me pergunto de onde veio o nome 'hash'. Que tal, por exemplo, "cache", então? :).
Michael
2
@ Michael Porque o hashcomando usa internamente uma tabela de hash para armazenar os mapeamentos. pt.wikipedia.org/wiki/Hash_table
jlliagre 08/08
10
Deve-se observar que, hashnão sendo bashespecífico, o comando se originou no shell Bourne no SVR2 (embora o recurso de caminhos de hash dos comandos venha cshantes disso) e é encontrado em todos os shells tipo Bourne e POSIX.
Stéphane Chazelas 8/08/13
1
Em vez de export PATH=$PATHlimpar a tabela, hash -rdeve ser suficiente.
ravron
1
Outro caso de uso é ao instalar uma segunda cópia de um programa em uma parte anterior do seu $ PATH. Você precisa hash -rou obterá a versão antiga porque $ PATH não mudou e, portanto, o Bash não percebe que poderia carregar o mesmo programa de um diretório anterior (de maior prioridade). Veja conda.pydata.org/docs/… para detalhes.
John Zwinck
37

Aqui está o uso clássico, simplificado:

# My PATH contains /home/rici/bin as well as the Usual Suspects:
# (the real one has lots more)
$ echo $PATH
/home/rici/bin:/usr/local/bin:/usr/bin:/bin

# I've installed a program called hello in /usr/local/bin
$ $ cat /usr/local/bin/hello
#!/bin/bash

echo Hello, world. I live at $0

# The program works.
$ hello
Hello, world. I live at /usr/local/bin/hello

# Now I want to create a better hello, just for me. I put it in
# my own bin directory, and according to my PATH, it should come first.
$ cp /usr/local/bin/hello ~/bin/hello

# So now I will try running it
$ hello
Hello, world. I live at /usr/local/bin/hello

# WTF? Oh, forgot to run hash.
# Tell bash to update where to look for hello
$ hash hello
$ hello
Hello, world. I live at /home/rici/bin/hello

# Ah, all is well.
rici
fonte
Conforme observado aqui, a atualização seletiva da tabela de hash pode ser chamada com um único comando hash hello.
Ioannis Filippidis
@ johntex: ok, mudou.
rici 9/08/14
Imagine o potencial de erros estranhos antes de saber que a tabela de hash existe! Existe uma lista de circunstâncias em que a tabela de hash é atualizada automaticamente?
benjimin
@ Benji: nunca é atualizado automaticamente (como um todo). Se você executar o bash no modo posix ou setopt -s checkhasho executável de hash de um comando não existir mais, a entrada de hash desse comando será atualizada. Mas observe que cada sessão do bash tinha sua própria tabela de hash, portanto, fechar a sessão e iniciar uma nova esvazia efetivamente o hash. ( hash -ré uma maneira mais fácil de fazer isso.)
rici 28/06
A atualização $PATHou o lançamento de um novo terminal do bash parecem limpar a tabela.
benjimin 28/06
17

Aqui está um uso útil de hash:

hash php 2> /dev/null || hash -p /usr/local/foobar/php/bin/php php 2> /dev/null

Significa: se o php não estiver no PATH, use

/usr/local/foobar/php/bin/
Gilles Quenot
fonte
8

Sim, o Manual de Referência do Bash diz:

Uma pesquisa completa dos diretórios em $ PATH é executada apenas se o comando não for encontrado na tabela de hash.

Mas você pode desativar o hash com set +h:

-h - Localize e lembre-se dos comandos (hash) conforme eles são procurados para execução. Essa opção é ativada por padrão.

Experimentar:

set +h
hash # prints bash: hash: hashing disabled
echo $? # prints 1

O mesmo é para hash -r, hash NAMEetc

A "detecção de comando" (como este ou que ) não funciona:

set -h
hash ls >/dev/null 2>&1 || echo "Please install ls" >&2 # prints nothing

set +h
hash ls >/dev/null 2>&1 || echo "Please install ls" >&2 # prints Please install ls

Você pode escrever algo como isto:

old_options="$-"
set -h
hash ls >/dev/null 2>&1 || echo "Please install ls" >&2
[[ "$old_options" =~ "h" ]] || set +h

ou (graças a @mikeserv) sem precisar atribuir novas variáveis ​​ou fazer testes:

set -h -- "-${-:--}" "$@"
hash ls >/dev/null 2>&1 || echo "Please install ls" >&2
set +h "$@"
Evgeny Vereshchagin
fonte
1
Para você old_options- eu costumo fazer algo assim: set -h -- "-${-:--}" "$@"; hash ...; set +h "$@"então tudo se encaixa automaticamente sem precisar atribuir novas variáveis ​​ou fazer testes ou o que for.
mikeserv
5

Fácil detecção se um comando está disponível:

CMD=gzip
if hash bzip2; then
    CMD=$_
fi
brablc
fonte