网络上有太多文章/资源教人们如何设置环境变量,PATH
以便他们可以使用java
或python
等的简写代替命令行界面中的绝对路径。
我想知道的是,当我们输入命令并按回车键时,幕后发生了什么(类似于在浏览器中输入 URL 时发生的情况)。
这是我的猜测:
$@
)$PS#
,$PROMPT
等等)我最想弄清楚的部分是命令查找。很明显,$PATH
被一些后台函数消耗掉了,用:
/;
作为分隔符,那么发生了什么?我们是否使用哈希表(键:文件的基本名称,值:文件的绝对目录名)将二进制文件存储在这些 PATH 或其他一些挂钩下?
注意:我最初认为它是哈希表,因为我可以用它[ -z hash [command] ]
来检查当前环境中是否有命令可用,但是当我使用时,hash | grep python
我没有从输出中得到任何东西,而which python
按预期工作。(我认为该机制可能是特定于 shell 的,但我想更深入地了解它。)
Mic*_*mer 12
正如您所怀疑的那样,确切的行为取决于 shell,但 POSIX 指定了基线功能级别。
标准 shell 命令语言(大多数 shell 实现其超集)的命令搜索和执行有很多情况,但我们暂时只对PATH
使用where 的情况感兴趣。在这种情况下:
应使用 PATH 环境变量搜索命令,如 XBD 环境变量中所述
和
如果搜索成功:
[...]
shell 在单独的实用程序环境中执行实用程序,其操作等同于调用
execl()
函数 [...],并将路径参数设置为搜索结果的路径名。
在不成功的情况下,执行失败并返回退出代码 127 和错误消息。
这种行为execvp
尤其与函数一致。所有exec*
函数都接受要运行的程序的文件名、一系列参数(将是argv
程序的 ),以及一组环境变量。对于使用PATH
查找的版本,POSIX 定义了:
参数文件用于构造一个路径名,用于标识新的进程映像文件 [...] 该文件的路径前缀是通过搜索作为环境变量 PATH 传递的目录获得的
此变量应表示某些函数和实用程序在搜索仅由文件名已知的可执行文件时应用的路径前缀序列。前缀应由 <colon> ( ':' ) 分隔。当一个非零长度的前缀应用于这个文件名时,如果前缀不是以 . 零长度前缀是指示当前工作目录的旧功能。它显示为两个相邻的字符 ( "::" ),作为列表其余部分之前的初始 <colon>,或作为列表其余部分之后的尾随 <colon>。严格遵守的应用程序应使用实际路径名(例如 .)来表示 PATH 中的当前工作目录。应从头到尾搜索该列表,将文件名应用于每个前缀,直到找到具有指定名称和适当执行权限的可执行文件。如果要查找的路径名包含 <slash>,则不应执行通过路径前缀的搜索。如果路径名以 <slash> 开头,则解析指定的路径(请参阅路径名解析)。如果 PATH 未设置或设置为 null,则路径搜索是实现定义的。
这有点密集,所以总结一下:
/
(斜杠,U+002F SOLIDUS),则按照通常的方式将其视为路径,并跳过此过程的其余部分。对于 shell,这种情况在技术上不会出现(因为 shell 规则已经处理过了)。PATH
在每个冒号处被分成几部分,然后从左到右处理每个组件。作为特殊(历史)情况,非空变量的空组件被视为.
(当前目录)。/
并检查该名称的文件是否存在,如果确实存在,则还检查有效的执行 (+x) 权限。如果这些检查中的任何一个失败,则该过程将移至下一个组件。否则,该命令将解析为该路径并完成搜索。PATH
,或者它不存在,请随心所欲。真正的 shell 将具有在此查找之前找到的内置命令,通常还有别名和函数。那些不与PATH
. POSIX 围绕这些定义了一些行为,你的 shell 可能有更多。
虽然可以依靠exec*
为您完成大部分工作,但实际上 shell 可能会自己实现此查找,尤其是出于缓存目的,但空缓存行为应该类似。壳在这里有相当广泛的范围,并且在极端情况下有微妙的不同行为。
正如您所发现的,Bash使用一个哈希表来记住它之前看到的命令的完整路径,并且可以使用该hash
函数访问该表。第一次运行命令时,它会搜索,当找到结果时,它会添加到表中,因此下次尝试时无需费心查找。
另一方面,在 zsh 中,PATH
通常在 shell 启动时搜索完整内容。查找表预先填充了所有发现的命令名称,因此通常不需要运行时查找(除非添加新命令)。当您尝试对以前不存在的命令进行 Tab 补全时,您会注意到这种情况。
非常轻量级的 shell,例如dash
,倾向于将尽可能多的行为委托给系统库,并且不会费心记住过去的命令路径。