【问题标题】:cd using empty string inconsistenciescd 使用空字符串不一致
【发布时间】:2016-04-19 15:56:32
【问题描述】:

根据"chdir" xopen specification,使用空字符串(“”)作为参数会导致错误(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 不变

为什么会有这么多差异?

【问题讨论】:

标签: shell posix sh ksh chdir


【解决方案1】:

检查第 4 节,Shell & Utilities 的 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

【讨论】:

  • 对于ksh93,根据我的测试,结果很喜忧参半:看看:pastebin.com/raw/MmMHRpMv
  • @CoolRaoul 谢谢,这几乎符合我的发现,因为只有 ksh93u+ 似乎表现出 IEEE 标准行为。我怀疑 ksh 团队注意到了规范并相应地调整了外壳。他们总是坚持标准。 - 顺便说一句,Solaris 9 中的 ksh93 从来都不是官方发行版的一部分。
  • “Solaris 9 中的 ksh93 从来不是官方发行版的一部分” 你是对的:它是 dtksh,我在少数几个(而且相当过时)上将其符号链接到 ksh出于脚本兼容性目的,Solaris 主机仍在我的范围内。
【解决方案2】:

你在这里比较苹果和梨。

您引用的 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 相同。

【讨论】:

    【解决方案3】:

    如前所述,您可以通过cd open group page 查找行为(我的笔记是要点):

    1. 如果没有给出目录操作数并且 HOME 环境变量为空或未定义,则默认行为是实现定义的,无需采取进一步的步骤。
      • 这不是真的,因为有一个目录操作数,它只是一个零长度的字符串
    2. 如果没有给出目录操作数并且 HOME 环境变量设置为非空值,则 cd 实用程序的行为就像在 HOME 环境变量中指定的目录被指定为目录操作数一样。
      • 见上文
    3. 如果目录操作数以 <slash> 字符开头,请将 curpath 设置为操作数并继续执行步骤 7。
      • 再次为假,进行下一步
    4. 如果目录操作数的第一个组件是点或点-点,则继续执行步骤 6。
      • 又是假的
    5. 从 CDPATH 的<colon> 分隔的路径名中的第一个路径名开始(请参阅环境变量部分),如果路径名非空,则测试该路径名的连接是否,如果该路径名是 <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,方法是删除任何不是前导字符的尾随 <slash> 字符,用单个 <slash> 替换多个非前导连续字符,并替换三个或更多以单个 <slash> 开头的 <slash> 字符。如果由于这种规范化,curpath 变量为空,则不应采取进一步的步骤。
        • 简单路径规范化。有趣的是,他们在最后一步中通过将路径显式显示为 noop 来说明路径是否为空,尽管我不确定这怎么可能发生,因为在这一步之前所有的相对路径都预先添加了 PWD。李>
    9. 如果 curpath 长于 {PATH_MAX} 个字节(包括终止的 null)并且目录操作数不超过 {PATH_MAX} 个字节(包括终止的 null),则 curpath 应尽可能从绝对路径名转换为等效的相对路径名。如果 PWD 的值为 curpath 的初始子字符串,则应始终认为这种转换是可能的,如果它还没有一个尾随 <slash>,则添加它。在其他情况下是否被认为可能是未指定的。如果 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 步,设置 curpath 到第 6 步中的空字符串,然后从第 7 步跳到第 10 步。我看不到任何其他方式可以使用空字符串调用 chdir,除非一个错误的实现将第 9 步按字面意思设置并设置curpath 到最后一句之后的空字符串。 Linux 上的 ksh93 和 AIX 上的 /bin/sh 不符合这些规则。通过这种方式,我会小心使用 cd 到可能评估零长度的路径,因为设置的 CDPATH 可能会奇怪地影响你正在尝试做的事情(尽管 CDPATH 无论如何都有意想不到和令人困惑的行为,并且应该大多数情况下不使用)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-13
      • 2011-07-01
      • 2022-10-02
      • 2014-05-27
      • 1970-01-01
      • 1970-01-01
      • 2015-04-26
      • 1970-01-01
      相关资源
      最近更新 更多