hash命令的目的是什么?


116

如果运行,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

根据手册页,哈希的目的是:

/ usr / bin / hash实用程序会影响当前shell环境记住所找到的实用程序位置的方式。根据指定的参数,它将实用程序位置添加到其记忆位置列表中,或者清除列表中的内容。如果未指定任何参数,它将报告列表的内容。该-r选项使外壳程序忘记所有记住的位置。

作为内置到shell中的实用程序不会通过哈希报告。

除了查看输入命令的次数外,看不到的实用程序hash

它甚至出现在thegeekstuff.com的前15个有用命令中

有什么hash用处?

Answers:


94

hash是bash内置命令。哈希表的一项功能是bash$PATH将结果缓存在内存中,从而使您不必在每次键入命令时都进行搜索。在明显使结果无效的事件(例如,修改$PATH)上清除该表

hash命令就是您与该系统进行交互的方式(无论您出于何种原因而需要)。

一些用例:

  • 就像您看到的那样,如果您不带任何参数键入它,它将打印出您击中了哪些命令的次数。这可能会告诉您最常用的命令。

  • 您也可以使用它来记住非标准位置的可执行文件。

例:

[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 ~]#

如果您只想$PATH通过键入名称而不是在目录中包含所有文件(如果将其添加到$PATH),便可以运行该目录之外的目录中的单个可执行文件,这可能会很有用。

别名通常也可以做到这一点,但是由于您正在修改当前shell的行为,因此别名不会映射到您启动的程序中。到单独的可执行文件的符号链接可能是此处的首选。hash是做到这一点的一种方法。

  • 您可以使用它来取消记住文件路径。如果新的可执行文件在较早的PATH目录中弹出或进入mv其他位置,并且您想强制bash再次找到它而不是记住它的最后一个位置,则这很有用。

例:

[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 ~]#

cp命令使ls可执行文件的新版本早些时候出现在我的计算机中,$PATH但没有触发哈希表的清除。我曾经hash -d选择性地清除ls哈希表中的条目。然后,Bash被迫$PATH再次浏览,当它这样做时,它在较新的位置(比以前运行的$ PATH更早)找到了它。

但是,您可以有选择地调用此“从中查找可执行文件的新位置$PATH”行为:

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

如果您想从哈希表中获得某些东西,而又不是100%可以注销然后再成功登录,或者想要保留对shell所做的一些修改,则通常只想这样做。

为了摆脱过时的映射,您还可以执行hash -r(或export PATH=$PATH)有效清除bash整个哈希表的操作。

有很多这样的小情况。我不知道我是否将其称为“最有用的”命令之一,但确实有一些用例。


我想知道“哈希”这个名字的来源。那“缓存”呢?:)。
迈克尔

2
@Michael因为该hash命令在内部使用哈希表来存储映射。en.wikipedia.org/wiki/Hash_table
jlliagre

10
应该注意的hashbash,命令不是特定的,它起源于SVR2的Bourne外壳程序(尽管命令的哈希路径的功能来自于此csh),并且可以在所有类似Bourne的外壳程序和POSIX外壳程序中找到。
斯特凡Chazelas

1
不必export PATH=$PATH清理表,hash -r就足够了。
ravron

1
另一个用例是在$ PATH的较早部分中安装程序的第二个副本。您需要hash -r或将获得旧版本,因为$ PATH不变,因此Bash并未意识到它可以从较早(优先级更高)的目录中加载相同的程序。有关详细信息,请参见conda.pydata.org/docs/…
约翰·兹温克

37

这是简化的经典用法:

# 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.

这里指出的哈希表的选择性更新可以用一个命令来调用hash hello
Ioannis Filippidis 2014年

@johntex:好的,更改了。
rici 2014年

在知道哈希表存在之前,想象一下潜在的奇怪错误!是否有自动刷新哈希表的情况列表?
本杰明

@benji:永远不会自动刷新(整体上)。如果您在posix模式下运行bash或setopt -s checkhash命令的哈希可执行文件不再存在,则该命令的哈希条目将被更新。但是请注意,每个bash会话都有自己的哈希表,因此关闭会话并开始一个新的会话实际上会清空该哈希。(hash -r这样做是更简单的方法。)
rici

更新$PATH或启动新的bash终端似乎都可以清理表。
本杰明

17

这是的有用用法hash

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

这意味着:如果php不在PATH中,请使用

/usr/local/foobar/php/bin/

7

是的,《Bash参考手册》说:

仅当在哈希表中未找到命令时,才对$ PATH中的目录进行完全搜索。

但是您可以使用禁用散列set +h

-h-查找并记住(哈希)命令,以查找它们以执行。默认情况下启用此选项。

尝试:

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

这同样适用于hash -rhash NAME

“命令检测”(例如thisthat)不起作用:

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

您可以这样写:

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

或(由于@mikeserv),而不必分配任何新变量或进行任何测试:

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

1
对于您的old_options事情-我通常会执行以下操作:set -h -- "-${-:--}" "$@"; hash ...; set +h "$@"因此,所有这些都将自动落入原位,而无需分配任何新变量或进行任何测试或其他操作。
mikeserv

5

轻松检测命令是否可用:

CMD=gzip
if hash bzip2; then
    CMD=$_
fi
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.