1

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

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

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

cd ""

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

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

为什么会有这么多差异?

4

3 回答 3

1

你在这里比较苹果和梨。

您引用的 xopen 规范是指 C-function 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 标准的一致性),肯定是 shell 实现中的一个错误(尽管与您所指的不同)。

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

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

于 2016-01-14T12:59:43.623 回答
1

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

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

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

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

综上所述

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

于 2017-03-19T23:53:38.657 回答
1

检查第 4 节,IEEE Std 1003.1™ 或Open Group Base Specification的Shell & Utilities

这包含cd的单独页面,其中显示:

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

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

于 2016-01-14T19:54:48.293 回答