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

JBa*_*lin 13 shell-script

在尝试获取文件的来源时,您是否不希望出现错误,指出该文件不存在以便您知道要修复什么?

例如,nvm建议将此添加到您的配置文件/rc:

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
Run Code Online (Sandbox Code Playgroud)

有了上面,如果nvm.sh不存在,你会得到一个“无声错误”。但是,如果您尝试. "$NVM_DIR/nvm.sh",输出将是FILE_PATH: No such file or directory

Sté*_*las 25

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

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

在这里做:

[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
Run Code Online (Sandbox Code Playgroud)

是一种情况,如果文件在那里,您想获取文件,如果文件不存在(或此处为空,则为-s)。

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

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

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

另一方面,

command . "$NVM_DIR/nvm.sh" 2> /dev/null
Run Code Online (Sandbox Code Playgroud)

其中command¹ 删除命令的特殊属性.(因此它不会在出错时退出 shell)将无法正常工作:

  • 它会隐藏.'s 的错误,但也会隐藏在源文件中运行的命令的错误
  • 它还会隐藏真正的错误条件,例如具有错误权限的文件。

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

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

    检查它在那里的文件,如果它是空的,仍然是它的来源。如果无法打开它仍然是致命错误(即使它在那里,或者在那里)。您可能会看到更多变体,例如[ -f "$file" ](存在并且是常规文件)、[ -r "$file" ](可读)或它们的组合。

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

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

  • command . "$file"

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

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

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


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


JBa*_*lin 5

的维护者nvm回复:

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

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

它更简单、更安全。

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