您怀疑,确切的行为取决于外壳,但是POSIX指定了基线功能级别。
标准shell命令语言(大多数shell都实现了父集)的命令搜索和执行有很多情况,但是我们暂时只对PATH
使用where的情况感兴趣。在这种情况下:
如XBD环境变量中所述,应使用PATH环境变量来搜索命令
和
如果搜索成功:
[...]
shell在单独的实用程序环境中执行该实用程序,其等效于调用path参数设置为搜索结果路径名的execl()
函数[...]
在不成功的情况下,执行失败,并返回127的退出代码和错误消息。
这种行为execvp
尤其与功能一致。所有exec*
功能都接受要运行的程序的文件名,一系列参数(将是argv
程序的参数)以及一组环境变量。对于使用PATH
查找的版本,POSIX定义了:
参数文件用于构建标识新过程映像文件的路径名,该文件的路径前缀是通过搜索作为环境变量PATH传递的目录而获得的。
PATH的行为在其他地方定义为:
该变量应表示某些功能和实用程序在搜索仅由文件名知道的可执行文件时应用的路径前缀的序列。前缀应由<colon>(':')分隔。当非零长度前缀应用于此文件名时,如果前缀未以结束,则应在前缀和文件名之间插入<slash>。零长度前缀是旧功能,用于指示当前工作目录。它以两个相邻的字符(“ ::”)的形式出现,在列表的其余部分之前显示为首字母<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
)倾向于将尽可能多的行为委派给系统库,而不必费心记住过去的命令路径。
PATH
之间的比较bash
,zsh
可以帮助我解决困惑!