cd使用空字符串不一致

时间:2016-01-14 10:48:52

标签: shell posix sh ksh chdir

根据"chdir" xopen specification,使用空字符串(“”)作为参数会导致错误(enoent):

[ENOENT]
A component of path does not name an existing directory or path is an empty string.

我使用命令检查了许多不同的操作系统和shell组合;

cd ""

最终调用“chdir”系统调用,argv == 2,argv [1]指向空字符串。

结果是Linux上(而不是AIX上)只有一些ksh93(所有版本)都会返回错误。 “/ bin / sh”总是成功但在AIX上它移动到$ HOME并且在linux cwd上没有变化

为什么会有这么多差异?

3 个答案:

答案 0 :(得分:1)

你在这里比较苹果和梨。

您引用的xopen规范是指C函数chdir

我使用的两个shell(bash和zsh)有一个内部命令cd,在两个shell中都有一个

cd ''

被解释为无操作。这在手册页中有解释,例如bash:

  

CDPATH中的空目录名与当前目录相同,即``。''。

所以这是预期的行为。请注意,您引用的标准并没有说明shell的cd命令。

我没有检查bash和zsh的开发人员如何实际执行cd命令,但如果他们想要遵守他们自己的规范,他们必须实现它(在C中)类似于:

if(argc == 0) {
    chdir(getenv("HOME"));
} else if(strlen(argv[1]) == 0) {
    chdir(".");
} else {
    chdir(argv[1]);
}

如果没有以这种方式完成,chdir命令的行为将取决于系统库的底层实现(是的,是否符合xopen标准), this 肯定是shell实现中的一个错误(尽管与你指的不同)。

更新:正如 CoolRaoul 在评论中正确注明的那样,我对bash联机帮助页的引用与此无关,因为它仅指CDPATH中的空元素,不是cd命令的空参数。虽然可以合理地假设两种情况下的效果应该相同,但没有明确规定。 zsh联机帮助页也是如此。在这两个联机帮助页中,也没有明确表示cd命令调用C函数chdir(虽然这也可以合理地假设),它们似乎也没有引用对xopen规范的任何符合性。至少对于bash和zsh,我认为我们可以放心地说cd ""的行为是未指定的。

顺便说一句,我也尝试过使用Cygwin附带的ksh(并将其识别为MIRBSD KSH R50),它的行为方式与bash和zsh相同。

答案 1 :(得分:1)

检查第4节, Shell& IEEE Std 1003.1™的实用程序Open Group Base Specification

这包含一个单独的page for cd,其中包含:

  

然后cd实用程序将执行与chdir()相当的操作   用curpath作为路径参数调用的函数。如果这些行动   由于任何原因失败,cd实用程序将显示适当的   错误消息和此步骤的其余部分不得执行。

这表明在cd ""上失败的ksh93实际上是按照规范工作的。这是我在Ubuntu 14.04上看到的,ksh版本AJM 93u+ 2012-08-01

答案 2 :(得分:1)

如前所述,您可以追踪cd open group page以查找行为(我的注释是要点):

  1. 如果没有给出目录操作数且HOME环境变量为空或未定义,则默认行为是实现定义的,不应采取进一步的步骤。
    • 这不是真的,因为有一个目录操作数,它只是一个零长度字符串
  2. 如果没有给出目录操作数且HOME环境变量设置为非空值,则cd实用程序的行为就像将HOME环境变量中指定的目录指定为目录操作数一样。
    • 见上文
  3. 如果目录操作数以<slash>字符开头,请将 curpath 设置为操作数,然后继续执行步骤7。
    • 再次假,转到下一步
  4. 如果目录操作数的第一个组件是点或点,请继续执行步骤6。
      再次
    • false
  5. <colon> - CDPATH的分隔路径名(请参阅环境变量部分)中的第一个路径名开始,如果路径名为非空,请测试该路径名的串联是否为<slash>个字符如果该路径名没有以<slash>字符结尾,则目录操作数命名目录。如果pathname为null,请测试dot,<slash>字符和操作数的串联是否为目录命名。在任何一种情况下,如果结果字符串命名现有目录,则将 curpath 设置为该字符串并继续执行步骤7.否则,使用CDPATH中的下一个路径名重复此步骤,直到测试完所有路径名。
    • 这里是我们吃肉的地方。根据规范,如果设置了CDPATH并且其中的路径名指向目录,则它将找到第一个现有路径名。因此,如果CDPATH为/foo:/bar:/baz/foo不存在,则cd将首先尝试/foo/并失败此步骤。然后它会尝试/bar/。如果/bar作为目录存在,则会将 curpath 设置为/bar/并继续。如果CDPATH为null,它将测试./以查看它是否指向某个目录(通常,因为这是您的pwd)。
  6. curpath 设置为目录操作数。
    • 换句话说,如果设置了CDPATH但其组件都不存在,它将只使用目录操作数,这是一个空字符串。
  7. 如果-P选项生效,请继续执行步骤10.如果 curpath 不以<slash>字符开头,请设置 curpath 到PWD值串联形成的字符串,如果PWD的值不以<slash>字符结尾,则为<slash>字符, curpath

    • 如果我们点击第6步,则会将 curpath 设置为PWD,因为它将是$(pwd)/
    • 本质上,通过此步骤,如果设置了CDPATH并将现有目录作为组件,则第一个现有组件将是 curpath 现在,否则 curpath 将是PWD(或可能PWD/./同样的效果)
  8. curpath 值应按如下方式转换为规范形式,从头到尾依次考虑每个组件:

    1. 将删除点组件和将它们与下一个组件分开的任何<slash>字符。
    2. 对于每个点 - 点组件,如果有前面的组件且它既不是根也不是点 - 点,那么:
      1. 如果前面的组件没有引用(在跟随符号链接的路径名解析的上下文中)到目录,则cd实用程序将显示相应的错误消息,不再采取进一步的步骤。
      2. 前面的组件,将删除前一个组件与点 - 点,点 - 点以及将点 - 点与下一个组件(如果有)分开的所有字符的所有<slash>个字符。
    3. 实现可以通过删除任何不是前导字符的尾随<slash>字符,用单个<slash>替换多个非前导连续字符来进一步简化 curpath ,以及使用单个<slash>替换三个或更多前导<slash>个字符。如果由于这种规范化, curpath 变量为null,则不应采取进一步的步骤。
      • 简单路径规范化。有趣的是,他们在最后一步中考虑如果路径通过明确地将其显示为noop而保持为null,尽管我不确定这可能是如何发生的,因为所有相对路径在此步骤之前都将PWD预先添加到它们之前。
  9. 如果 curpath 长于{PATH_MAX}个字节(包括终止空值)且目录操作数不超过{PATH_MAX}个字节(包括终止空值),则 curpath < / strong>应尽可能从绝对路径名转换为等效的相对路径名。如果PWD的值(如果尚未添加尾随<slash>)是 curpath 的初始子字符串,则应始终认为此转换是可能的。在其他情况下是否认为可能是未指定的。如果 curpath 不长于{PATH_MAX}字节或目录操作数长于{PATH_MAX}字节,则实现也可能应用此转换。
    • 如果路径过长,那么除了让路径再次相对之外,似乎没什么其他的。
  10. 然后,cd实用程序将执行与使用 curpath 作为路径参数调用的chdir()函数等效的操作。如果这些操作因任何原因失败,则cd实用程序将显示相应的错误消息,并且不执行此步骤的其余部分。如果-P选项无效,则PWD环境变量应设置为 curpath 在进入步骤9时(即,在转换为相对路径名之前)所具有的值。如果-P选项生效,则PWD环境变量应设置为由pwd -P输出的字符串。如果对新目录或该目录的任何父目录没有足够的权限来确定当前工作目录,则不指定PWD环境变量的值。
    • 这里是chdir实际发生的地方。
  11. 总结

    因此,本质上,标准的cd ''命令应该cd到CDPATH的第一个现有组件(如果已设置),否则应该cd到当前目录。如果使用cd -P '',它还会从路径中删除符号链接。这样,chdir只应该用空字符串调用,如果CDPATH是非空的,但不存在任何组件,并调用cd -P '',因为它将通过步骤5,设置 curpath 在步骤6中为空字符串,然后从第7步跳到第10步。我没有看到任何其他方式使用空字符串调用chdir,除非执行错误步骤9太字面意思,并将 curpath 设置为最后一句后面的空字符串。 Linux上的ksh93和AIX上的/ bin / sh不符合这些规则。通过这种方式,我要小心将cd用于可能评估零长度的路径,因为正在设置的CDPATH会奇怪地影响你正在尝试做的事情(虽然CDPATH有意外和混乱的行为无论如何,不​​应该在大多数情况下使用。)