Qual é o propósito do comando hash?

103

Se você executar hash , o caminho de todos os comandos será executado desde que o hash foi redefinido pela última vez ( 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 man pages, o objetivo do hash é:

The /usr/bin/hash utility affects the way the current shell environment remembers the locations of utilities found. Depending on the arguments specified, it adds utility locations to its list of remembered locations or it purges the contents of the list. When no arguments are specified, it reports on the contents of the list. The -r option causes the shell to forget all remembered locations.

Utilities provided as built-ins to the shell are not reported by hash.

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

Foi até mesmo apresentado nos principais 15 comandos úteis do thegeekstuff.com

De que maneiras o hash é útil?

    
por spuder 08.08.2013 / 20:12

5 respostas

77

hash é um comando interno do bash. A tabela de hash é um recurso do bash que impede que ele tenha que pesquisar $PATH toda 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 comando hash é exatamente como você interage com esse sistema (por qualquer motivo que você ache que precisa).

Alguns casos de uso:

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

  • Você também pode usá-lo para lembrar executáveis em locais não 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ê tiver apenas um único executável em um diretório fora de $PATH que deseja executar, basta digitar o nome em vez de incluir tudo nesse diretório (o que seria o efeito se você o adicionasse a $PATH ).

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

  • Você pode usá-lo para cancelar o caminho dos arquivos. Isso é útil se um novo executável aparecer em um diretório PATH anterior ou se obtiver mv 'd em algum outro lugar e você quiser forçar o bash a sair e encontrá-lo novamente, em vez do último lugar em que 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 comando cp fez com que uma nova versão do executável ls aparecesse mais cedo no meu $PATH , mas não acionou uma limpeza da tabela de hash. Eu usei hash -d para remover seletivamente a entrada para ls da tabela de hash. O Bash foi forçado a procurar por $PATH novamente e, quando o fez, encontrou-o no local mais novo (anteriormente em $ PATH do que estava em execução antes).

Você pode invocar seletivamente esse comportamento "encontrar nova localização de executável no $PATH ":

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

Você geralmente só quer fazer isso se quiser algo fora da tabela de hash e não foi 100% que você poderia fazer logout e depois voltar com sucesso, ou você queria preservar algumas modificações que você fez na sua concha.

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

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

    
por 08.08.2013 / 21:12
33

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.
    
por 08.08.2013 / 21:26
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

Isso significa que: se o php não estiver no PATH, use

/usr/local/foobar/php/bin/
    
por 08.08.2013 / 20:17
6

Sim, Manual de Referência do Bash diz:

A full search of the directories in $PATH is performed only if the command is not found in the hash table.

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

-h - Locate and remember (hash) commands as they are looked up for execution. This option is enabled by default.

Tente:

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

O mesmo é para hash -r , hash NAME etc

Uma "detecção de comando" (como esta ou isso ) 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 assim:

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 nenhum teste:

set -h -- "-${-:--}" "$@"
hash ls >/dev/null 2>&1 || echo "Please install ls" >&2
set +h "$@"
    
por 29.07.2015 / 11:59
5

Detecção fácil se um comando está disponível:

CMD=gzip
if hash bzip2; then
    CMD=$_
fi
    
por 17.10.2015 / 15:22

Tags