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