在获取文件之前什么时候(或为什么)应该检查文件是否存在?

在获取文件之前什么时候(或为什么)应该检查文件是否存在?

当尝试获取文件时,您是否不希望出现一个错误,提示该文件不存在,以便您知道要修复什么?

例如,非易失性建议将其添加到您的配置文件/rc 中:

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm

如上所述,如果nvm.sh不存在,您将收到“无提示错误”。但如果你尝试. "$NVM_DIR/nvm.sh",输出将会是FILE_PATH: No such file or directory

答案1

在 POSIX shell 中,.是一个特殊的内置命令,因此它的失败会导致 shell 退出(在某些 shell 中,例如bash,它仅在 POSIX 模式下完成)。

什么是错误取决于 shell。并非所有这些都在解析文件时因语法错误而退出,但大多数会在无法找到或打开源文件时退出。我不知道如果源文件中的最后一个命令返回非零退出状态(当然,除非该errexit选项打开),任何命令都会退出。

在这里做:

[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

在这种情况下,如果文件存在,您希望获取该文件的源,如果文件不存在(或者此处为空-s),则不获取该文件的源。

也就是说,如果该文件不存在,则不应将其视为错误(POSIX shell 中的致命错误),该文件被视为可选文件。

如果文件不可读或者是一个目录,或者(在某些 shell 中)如果在解析文件时出现语法错误,这仍然是一个(致命)错误,这将是应该报告的真正错误情况。

有些人会认为存在竞争条件。但它唯一意味着,如果在 和 之间删除文件,shell 将退出并出现错误[.但我认为,在脚本执行时,此固定路径文件会突然消失,认为这是一个错误是有效的。跑步。

另一方面,

command . "$NVM_DIR/nvm.sh" 2> /dev/null

其中command¹ 删除了特别的命令的属性.(因此它不会在错误时退出 shell)将不起作用:

  • 它会隐藏.的错误,还会隐藏源文件中运行的命令的错误
  • 它还会隐藏真正的错误情况,例如文件具有错误的权限。

其他常见语法(例如,参见grep -r /etc/default /etc/init*Debian 系统上尚未转换的初始化脚本systemd(其中EnvironmentFile=-/etc/default/service用于指定可选环境文件))包括:

  • [ -e "$file" ] && . "$file"

    检查文件是否存在,如果它是空的,仍然获取它。如果无法打开它仍然是致命错误(即使它在那里,或者曾经在那里)。您可能会看到更多变体,例如[ -f "$file" ](exists and is a常规的文件)、[ -r "$file" ](可读)或这些的组合。

  • [ ! -e "$file" ] || . "$file"

    稍微好一点的版本。更清楚地表明文件不存在是正常情况。这也意味着$?将反映最后运行的命令的退出状态$file(在前面的情况下,如果您得到1,您不知道它是因为$file不存在还是该命令失败)。

  • command . "$file"

    期望该文件存在,但如果无法解释则不要退出。

  • [ ! -e "$file" ] || command . "$file"

    上述的组合:如果文件不存在也没关系,对于 POSIX shell,会报告打开(或解析)文件的失败,但不是致命的(这可能更适合~/.profile)。


1 注意:但是,除非在仿真中,否则zsh不能command这样使用;sh请注意,在 Korn shell 中,source实际上是 的别名command .,是 的非特殊变体.

答案2

维护者的nvm回应:

只需删除文件即可轻松卸载nvm;强制进行额外的工作(追踪源 nvm 的行)似乎并不特别有价值。

我的解释(结合Stéphane的出色解释和Kusalananda的评论):

它更简单、更安全。

它可以防止 POSIX shell 在启动时由于丢失文件(由于各种原因)而退出。使用非 POSIX(例如 bash)shell 的用户可以根据需要删除条件。

答案3

作为杰巴林斯蒂芬·查泽拉斯已经指出,在 POSIX shell 中,获取不存在的文件会导致登录失败。

但是添加测试来查看文件是否存在,然后尝试获取它可能会导致称为竞争条件的情况。如果和nvm.sh之间发生了一些变化,它就会导致他们试图阻止的错误,尽管这种情况很少见。[ -s nvm.sh ]. nvm.sh

一般来说,防止竞争条件的方法是只尝试你想做的事情,然后在失败时处理错误,例如

. "$NVM_DIR/nvm.sh" || echo "Sourcing $NVM_DIR/nvm.sh failed" >&2

事实证明,这在 POSIX shell 中不起作用,因为如上所述,.失败将导致 shell 在任何错误处理运行之前立即退出。

我的回答认为 POSIX shell 与这个问题无关,因为.bash_profile不应该在 POSIX 模式下运行。所以我们无论如何都可以执行上面的代码。

为了最安全,我们可以确保 POSIX 模式不起作用,或者使用中描述的技术确保禁用 POSIX 模式https://unix.stackexchange.com/a/383581/3169

Stéphane 的答案对如何处理所有 POSIX shell 提供了一些有用的建议,我认为这是 nvm 作者的意图,但与这里提出的问题略有不同,这就是为什么我们有多种可能的方法,具体取决于您的目标是什么。

相关内容