PATH搜索是否包含符号链接?


9

POSIX Shell标准在此站点上说

http://pubs.opengroup.org/onlinepubs/9699919799/

有关shell如何PATH用于查找可执行文件的信息:

“应从头到尾搜索列表,将文件名应用于每个前缀,直到找到具有指定名称和适当执行权限的可执行文件为止。”

嗯,这在实际的POSIX实现中似乎不是这样工作的:

man which 说:

“返回参数(在严格符合POSIX的外壳程序中以命令形式给出)的情况​​下,将在当前环境中执行的文件(或链接)的路径名。它通过在PATH中搜索与文件名匹配的可执行文件来实现。参数。它不遵循符号链接。”

好,让我们看看这种情况:

$ pwd /home/mark

$ echo $PATH /home/mark/bin:...

$ ls -l bin/foobar
lrwxrwxrwx 1 mark mark 18 Dec 12 22:51 bin/foobar -> /home/mark/foobar1
$ touch foobar1
$ which foobar
$ chmod a+x foobar1
$ which foobar
/home/mark/bin/foobar

好的,这是一个PATH带有正确名称的符号链接,并且据报告ls是可执行的。

which 根本不看它,只对它指向的内容感兴趣。

尽管事实上两者都man which明确表示它不遵循符号链接(并且确实我们没有看到它,因为which foobar未打印foobar1),并且上面引用的POSIX shell文档也从未提及PATH算法中的以下符号链接。

那么,which现有的外壳是错误的,还是我不理解文档?

澄清:

我知道并可以解释现有的行为。我的问题不是“这是如何工作的?”。我知道

我的问题是关于文档:按照我引用的文档,我的错误在哪里?还是文档错误?

动机:为什么我要在乎?

好吧,我是一个实施者。不同的实施者有不同的要求。对我来说,要求是必须严格遵循当前POSIX标准的字眼(或者,更准确地说,应该是最好的字眼,因为该标准本身有些错误)。就像上帝的道一样。

现在,标准措辞非常清楚-没有提到以下符号链接,在许多其他地方的地方,也需要提到它。因此,在这种情况下,请不要。

但是,我总是要仔细检查操作方式dashbash行为,以确保。当然,现在,这里也有一个小问题,dash即使它被称为POSIX,也有许多与POSIX一致的小错误。 bash,我还没有发现POSIX的任何错误,但是... bash实际上不是POSIX,它的意义远不止于此。

所以你有它。这就是为什么我在乎。


您不了解:不会遵循文件上的符号链接。$PATH可以包含符号链接。尝试which sh
Ipor Sircer

可以,但就我而言$PATH,没有任何符号链接。
user322908

在几乎所有情况下,符号链接都是透明的。通常明确提及未使用它们的情况(例如,像那样的系统调用lstat(2)),通常不会在它们后面说明。例如,open(2)当谈论的行为时,对的描述仅提及符号链接O_CREAT | O_EXCL。无需说明将打开目标文件。
巴马尔

Answers:


10

符号链接本身的权限无关紧要。如果尝试过,甚至都无法更改它们。

重要的是基础文件的权限。

在PATH中的目录包含指向可执行文件的符号链接是很好的。实际上,PATH中的许多可执行文件很可能是符号链接。例如,在类似debian / ubuntu的系统上:

$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Jan 23  2017 /bin/sh -> dash

文献资料

来自man chmod

chmod永远不会更改符号链接的权限;chmod系统调用无法更改其权限。这是没有问题的,因为从不使用符号链接的权限。 但是,对于命令行上列出的每个符号链接,chmod都会更改指向文件的权限。相反,chmod忽略在递归目录遍历期间遇到的符号链接。[重点已添加。]

该外壳程序具有测试,-x以确定文件是否可执行。让我们尝试一下:

$ ls -l
total 0
lrwxrwxrwx 1 john1024 john1024 7 Dec 12 23:36 foo -> foobar1
-rw-rw---- 1 john1024 john1024 0 Dec 12 23:36 foobar1
$ [ -x foo ] && echo foo is executable
$ chmod +x foobar1
$ [ -x foo ] && echo foo is executable
foo is executable

因此,就像您在中找到一样which,除非基础文件是可执行文件,否则外壳程序不会将其视为软链接可执行文件。

哪个工作

在Debian系统上,which是一个shell脚本。该代码的相关部分是:

 case $PROGRAM in
  */*)
   if [ -f "$PROGRAM" ] && [ -x "$PROGRAM" ]; then
    puts "$PROGRAM"
    RET=0
   fi
   ;;
  *)
   for ELEMENT in $PATH; do
    if [ -z "$ELEMENT" ]; then
     ELEMENT=.
    fi
    if [ -f "$ELEMENT/$PROGRAM" ] && [ -x "$ELEMENT/$PROGRAM" ]; then
     puts "$ELEMENT/$PROGRAM"
     RET=0
     [ "$ALLMATCHES" -eq 1 ] || break
    fi
   done
   ;;
 esac

如您所见,它使用-x测试来确定文件是否可执行。

POSIX指定-x测试如下:

-x pathname
如果路径名解析为文件的现有目录条目,则该文件将被授予执行文件权限(或搜索文件,如果它是目录)的文件的现有目录条目,如文件读取,写入和创建中所定义。如果无法解析路径名,或者如果路径名解析为文件的现有目录条目,则该文件的执行(或搜索)权限将不被授予,则为False。[重点已添加。]

因此,POSIX检查路径名解析为什么。换句话说,它接受符号链接。

POSIX执行功能

POSIX exec函数如下符号链接。POSIX规范继续详细说明错误情况,如果符号链接是圆形的或过深,它可能会报告错误情况,例如:

[ELOOP]
在解析路径或文件参数期间遇到的符号链接中存在循环。

[ELOOP]
在解析路径或文件参数期间遇到了超过{SYMLOOP_MAX}个符号链接。
[ENAMETOOLONG]
由于在path参数的解析中遇到符号链接,因此替换的路径名字符串的长度超过了{PATH_MAX}。


我知道您在回答中写的所有内容。我知道事情是“有效的”。那不是我的问题。我的问题是关于文档。指出我不了解文档的位置。或告诉我该文档不正确。
user322908

@ user322908在大多数系统上,which是一个shell脚本。因此,很可能仅使用-x我显示的测试。根据POSIX,-x测试文件是否“解析”为可执行文件。如果您看到的是不同的东西,那么您在哪里看?
John1024 '17

谢谢,很抱歉您如此痛苦。我意识到我的问题与99%的问题不同,因此很难理解我的追求。再说一次,我对“如何” which工作,是否工作-x或其他不感兴趣。我想知道我在哪里没有正确遵循我引用的文档。
user322908

4
@ user322908 POSIX exec函数遵循符号链接。这似乎很清楚,符号链接文件可以在POSIX下执行。
John1024 '17

2
我还检查了Ubuntu 17.10 man which,它说“它不能规范化路径名”。这并不意味着它不遵循链接。正如您所观察到的,这仅意味着它不会“规范化”名称。
John1024 '17

3

在这种情况下,透明地跟随符号链接而无需规范化最终路径。换句话说,which不在乎是否/home/mark/bin是符号链接。它关心的是文件是否/home/mark/bin/foobar存在。它不需要手动压平路径上的符号链接-操作系统可以自己做到这一点。

确实,当which询问的文件信息时/home/mark/bin/foobar,OS注意到/home/mark/bin是一个符号链接,跟随它,并成功foobar在目标目录中找到了。

除非程序使用open(…, O_NOFOLLOW)fstatat(…, AT_SYMLINK_NOFOLLOW)访问文件,否则这是默认行为。

[评论合并]

尽管您说shell实用程序是逐个案例进行的,但对于内核syscalls而言却并非如此:默认情况下,所有与文件相关的调用遵循符号链接,除非给出了“ nofollow”标志。(lstat甚至在除最后一个以外的所有路径组件中都遵循符号链接。)

当规范中未明确提及如何处理符号链接时,则表示将使用默认行为。也就是说,遵循路径算法的外壳不会手动解析符号链接,不会显式选择退出操作系统。(它只是将每个$ PATH组件与可执行文件名称连接在一起。)

当which(1)手册页说它不遵循符号链接时,可能意味着几件事情,但是GNU coreutils版本以这种方式声明它:

当其中一个等效目录包含带有符号链接的路径时,它们将认为两个等效目录是不同的。

范围要窄得多–它仅意味着which不会尝试手动规范化所有路径以清除重复项,但这并不意味着该工具通常会选择退出OS的符号链接。例如,如果/bin是一个符号链接/usr/bin,运行which -a sh将返回两个 /bin/sh/usr/bin/sh


是的,谢谢,我知道所有这些。我的问题不是事情如何“起作用”。我知道他们是如何工作的。那不是我的意思。我的意思是关于文档。我在哪里错误地遵循了文档。还是文档不正确。
user322908

2
您对文档的理解不正确-如果未提及以下符号链接,则表示它不会手动解析符号链接,但常规的操作系统行为仍然适用。GNU which手册页对此进行了不同的说明:“当两个等效目录之一包含带有符号链接的路径时,它们将认为两个等效目录是不同的。”
user1686 '17

好的,谢谢。我试图理解...但是...请原谅我的脖子上的痛苦:“常规的OS行为”并不总是隐含地遵循符号链接。有很多实用程序没有。视具体情况而定。
user322908

1
除非您指定AT_SYMLINK_NOFOLLOW或类似名称,否则所有内核调用(chdir,open,chmod,execve ...)都将在路径和尾部都遵循符号链接。(lstat是唯一未在尾部取消引用符号链接,但仍对其余路径取消引用的引用。)因此,默认行为是遵循符号链接。例如,当外壳程序调用时execve("/home/mark/bin/foobar", …),它将导致遵循所有符号链接。
user1686 '17

好的,我想我买的是execve参数,实际上,在我的实现中,是execl()一样的东西。请在您的回答中加入这一点,我将接受。
user322908

1

该外壳程序遵循其文档说明,因为它遵循路径名解析的规则。 which符合其文档。两者做的事情略有不同。

输出which是链接的文件名和路径,而不是符号链接指向的路径。这是在手册页中阐明的。

当执行命令时,链接按照“ 4.13路径名解析” 中的 “跟随” 。执行文件的相关子句为:

在所有其他情况下,系统应使用符号链接的内容为其余路径名(如果有的话)加上前缀,但如果符号链接的内容为空字符串,则任一路径名解析均将失败,并且函数将报告[ENOENT错误和实用程序编写等效的诊断消息,或使用包含符号链接的目录的路径名代替符号链接的内容。如果符号链接的内容仅由字符组成,则剩下的路径名的所有前导字符都应从结果组合路径名中省略,而仅保留符号链接内容中的前导字符。在发生前缀的情况下,如果组合长度超过{PATH_MAX},并且实现将其视为错误,路径名解析将失败,函数将报告[ENAMETOOLONG]错误,并且实用程序将写入等效的诊断消息。否则,解析的路径名应为刚创建的路径名的分辨率。如果生成的路径名不是以a开头,则该路径名的第一个文件名的前任将被视为包含符号链接的目录。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.