小心bash的hash坑了你

你是否会有这样的需求呢:在Linux/Mac系统下,需要安装同一软件的不同版本,然后通过修改PATH环境变量,使新安装的版本生效?这个时候,可能就要小心了。我就遇到了这样的一个问题。

奇怪的问题

Mac系统已经自带有Python,然而由于与其他软件的兼容问题,需要同时安装原生的Python。于是:

1
2
3
4
5
6
7
8
$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
$ which python
/usr/bin/python
$ brew install python
...
$ which python
/usr/local/bin/python

到这里,原生的Python已经安装完成。新安装的Python的软链接放在/usr/local/bin目录下。此时执行python命令,结果却让我觉的简直是日了狗了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ python
Python 2.7.6 (default, Sep 9 2014, 15:04:36)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.39)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> quit()
$
$ /usr/bin/python
Python 2.7.6 (default, Sep 9 2014, 15:04:36)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.39)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> quit()
$
$ /usr/local/bin/python
Python 2.7.10 (default, Aug 30 2015, 11:33:20)
[GCC 4.2.1 Compatible Apple LLVM 6.1.0 (clang-602.0.53)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> quit()

从上面Python打印的时间戳不难看出,虽然which已经正确的找到了新安装的Python,但是当执行python命令是,实际执行的仍然是旧版的Python。但是此时,如果我再重新开一个Terminal,执行python命令,却可以正确的执行新安装的python命令。

Bash的命令缓存

缓存?经过一番搜索,Bash果然有所谓的命令缓存。查阅Bash的manual,搜索hash关键字,可以得到如下的说明:

After a command has been split into words, if it results in a simple command and an optional list of arguments, the following actions are taken.
If the command name contains no slashes, the shell attempts to locate it.If there exists a shell function by that name, that function is invoked as described above in FUNCTIONS. If the name does not match a function, the shell searches for it in the list of shell builtins. If a match is found, that builtin is invoked.
If the name is neither a shell function nor a builtin, and contains no slashes, bash searches each element of the PATH for a directory containing an executable file by that name. Bash uses a hash table to remember the full pathnames of executable files (see hash under SHELL BUILTIN COMMANDS below). A full search of the directories in PATH is performed only if the command is not found in the hash table. If the search is unsuccessful, the shell prints an error message and returns an exit status of 127.

根据说明,可以通过内置命令hash来管理相应的hash table:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ hash -l # 列出当前的hash table
builtin hash -p /usr/bin/which which
builtin hash -p /usr/local/bin/brew brew
builtin hash -p /usr/bin/man man
builtin hash -p /bin/ls ls
builtin hash -p /usr/bin/python python
$ hash -d python # 删除hash table中的python条目
$ hash -l
builtin hash -p /usr/bin/which which
builtin hash -p /usr/local/bin/brew brew
builtin hash -p /usr/bin/man man
builtin hash -p /bin/ls ls
$ python
Python 2.7.10 (default, Aug 30 2015, 11:33:20)
[GCC 4.2.1 Compatible Apple LLVM 6.1.0 (clang-602.0.53)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> quit()

除了上面的-l和-d参数,还有-r参数。该参数用于删除当前hash table中的所有条目。

1
2
3
$ hash -r
$ hash -l
hash: hash table empty

总结

在安装了新软件同时系统中有旧版本的软件共存时,记得执行hash -r来让新安装的软件生效。