当前位置:首页>Linux>Linux Bash 的命令路经缓存机制(hash)

Linux Bash 的命令路经缓存机制(hash)

  • 2026-10-21 13:29:55
Linux Bash 的命令路经缓存机制(hash)

昨天笔者有个应用要用到 nodejs,在安装 fnm[1] 这个 node 版本管理器时查看了一下它的安装脚本。里面有一段代码引起了笔者的注意。

check_dependencies() {echo "Checking dependencies for the installation script..."echo -n "Checking availability of curl... "if hash curl 2>/dev/null; thenecho "OK!"elseecho "Missing!"        SHOULD_EXIT="true"fi# 其它略...}

这里检查命令是否存在是通过 Bash 内建命令 hash 来完成的,笔者用的是 command -v,所以对这个 hash 命令比较好奇。

bash 维护一个内部哈希表,用于缓存已找到的命令的完整路径。当输入一个命令时:

  • 首先检查哈希表
  • 如果找到,直接使用缓存的路径
  • 如果没找到,搜索 $PATH 并添加到哈希表

当执行命令时,bash 会自动更新哈希表:

# 第一次执行 ls 会搜索 PATH$ ls# bash 自动执行:hash ls# 后续执行 ls 使用缓存路径$ ls

hash 命令就是用来手动管理这个哈希表的命令。它用于缓存和查看哈希表中命令的完整路径。 bash 通过哈希表记住命令的完整路径名,避免在后续调用时重复进行 $PATH 搜索,从而提高命令执行效率。

hash [-r] [-p 文件名] [-dt] [命令名]

例如:

# 搜索 PATH, 并缓存找到的命令 lshash ls# 命令不存在hash cmd_not_existbash: hash: cmd_not_exist: 未找到

选项:

选项
功能描述
-r
使 shell 忘记所有已记住的命令位置。
这相当于清空整个哈希表。
-p 文件名
抑制路径搜索,直接将指定的 文件名 作为 命令名 的位置。
这会强制将 文件名 与 命令名 关联,而不检查它是否真的可执行。
-d
使 shell 忘记每个指定 命令名 的已记住位置。
只删除指定的命令缓存。
-t
显示每个 命令名 对应的完整路径名。
如果与多个 命令名 一起使用,会在每个完整路径前打印对应的命令名。
-l
以可重新用作输入的格式显示输出。这对于保存和恢复哈希表很有用。

-t、-d 和 -p 这三个选项(作用于 命令名 参数的选项)是互斥的,一次只能有一个生效。如果提供了多个,优先级如下:

  1. -t 优先级最高(显示)
  2. -p 优先级次之(指定路径)
  3. -d 优先级最低(删除)

返回码:

返回码
含义
0
成功执行
非 0
命令未找到或提供了无效选项

所有它可以用于判断命令是否存在。

1. 选项示例

1:查看当前哈希表

# 显示所有已缓存的命令及其命中次数bash-5.3$ hash命中 命令   2 /bin/cp   3 /usr/bin/man   1 /bin/ls   2 /Users/xxx/miniconda3/envs/notebook/bin/python   1 /usr/bin/vim
  • 命中列:该命令被成功执行的次数
  • 命令列:命令的完整路径

2:显示特定命令的完整路径

bash-5.3$ hash -t ls/bin/ls# 显示多个命令bash-5.3$ hash -t ls grep findls /bin/lsgrep /usr/bin/grepfind /usr/bin/find

如果命令从未被 hash 过, 则:

bash-5.3$ hash -t grepbash: hash: grep: 未找到# 缓存 grepbash-5.3$ grepbash-5.3$ hash -t grep/usr/bin/grep

3:强制指定命令路径

# 强制将 /usr/local/bin/myls 与 ls 关联$ hash -p /usr/local/bin/myls ls# 现在执行 ls 会调用 /usr/local/bin/myls$ ls

4:删除特定命令缓存

# 删除 ls 的缓存bash-5.3$ hash -d ls# 验证 ls 已被删除bash-5.3$ hash命中 命令   2 /usr/bin/grep   2 /bin/cp   2 /usr/bin/find   3 /usr/bin/man   2 /Users/xxx/miniconda3/envs/notebook/bin/python   1 /usr/bin/vim

5:清空整个哈希表

# 清空所有缓存bash-5.3$ hash -r# 哈希表已空bash-5.3$ hashhash: 哈希表为空

6:以可重用格式显示

bash-5.3$ hash -lbuiltinhash -p /usr/bin/grep grepbuiltinhash -p /usr/bin/find findbuiltinhash -p /bin/ls ls

这种格式的输出可以直接复制粘贴回终端来重建哈希表。

2. 自动管理机制

1. PATH 变更时自动清空

# 查看当前哈希表bash-5.3$ hash命中 命令   1 /usr/bin/grep   1 /usr/bin/find   1 /bin/ls# 修改 PATHbash-5.3$ PATH=/usr/local/bin:$PATH# 哈希表被自动清空bash-5.3$ hashhash: 哈希表为空

2. 命令移动或删除时

如果缓存的命令被移动或删除,bash 会在下次执行时发现并重新搜索:

3. 应用场景

1:优化脚本性能

#!/bin/bash# 在脚本开始缓存常用命令hash awk sed grep cut 2>/dev/null# 后续多次调用这些命令时不需要搜索 PATHfor file in *.txt; do    grep "pattern""$file" | awk '{print $1}'done

2:调试命令路径问题

# 检查命令的实际路径$ hash -t python/usr/bin/python# 如果怀疑有多个版本$ type -a pythonpython is /usr/bin/pythonpython is /usr/local/bin/python# 清空缓存重新测试$ hash -r$ python --version$ hash -t python# 现在显示的是实际使用的路径

3:临时替换命令

# 创建自定义的 ls 版本$ echo'echo "Custom ls"' > /tmp/myls$ chmod +x /tmp/myls# 临时替换 ls$ hash -p /tmp/myls ls$ lsCustom ls# 恢复原来的 ls$ hash -d ls$ ls# 正常列出文件

4. 总结

hash 命令是 bash 的一个性能优化工具,它通过缓存命令路径减少 $PATH 搜索开销。主要用途包括:

  1. 性能优化:减少重复命令的路径查找
  2. 调试工具:查看命令的实际执行路径
  3. 路径控制:临时指定命令的路径
  4. 状态监控:查看命令使用频率

注意事项:

  1. 哈希表是进程本地的:每个 shell 会话有自己的哈希表
  2. 子进程不继承:在子 shell 或脚本中修改哈希表不影响父 shell
  3. 命令不可执行也能缓存:-p 选项可以缓存任何路径,即使文件不可执行
  4. 别名优先:如果命令有别名,会先执行别名,不会触发哈希查找

虽然日常交互中很少需要手动使用 hash 命令,但了解它的工作原理有助于理解 shell 如何高效地执行命令,并在需要调试命令路径问题时提供有用的工具。

最后是相关命令的比较。

命令
功能
特征
type
显示命令的类型和位置
type
 显示命令如何被解释,hash 管理路径缓存
which
显示命令的完整路径
which
 总是搜索 PATH,hash 可能返回缓存结果
command -v
标准方式查找命令
更便携,但不会更新哈希表
# 比较不同命令$ hash -t ls      # 显示缓存中的路径/bin/ls$ which ls        # 总是搜索 PATH/bin/ls$ type -a ls         # 显示命令类型和所有路径ls is hashed (/bin/ls)$ command -v ls   # POSIX 标准方式/bin/ls

参考资料

[1] 

fnm: https://github.com/Schniz/fnm

最新文章

随机文章