尽管出现错误,如何保持 dash shell 脚本运行?

尽管出现错误,如何保持 dash shell 脚本运行?

我有一个破折号 shell 脚本,旨在运行到文件末尾:

#!/usr/bin/dash
# File b_shell
. a_nonexistent_file
echo "I want to print this line despite any previous error."

但是,它在无法获取不存在的文件后停止运行:

$ ./b_shell
./b_shell: 3: .: a_nonexistent_file: not found

我尝试过包括set -e和在内的候选解决方案|| true,这些解决方案可以在互联网上轻松找到,但无济于事。

附加说明:

  1. 我不被允许使用其他 shell,例如 Bash、tcsh 等。请不要让我切换。
  2. 我注意到其他一些“未找到”错误不会停止脚本,如下所示。
#!/usr/bin/dash
# File b_shell_ok
a_nonexistent_command
ls a_nonexistent_file
echo "This line is printed despite any previous error."
$ ./b_shell_ok
./b_shell_ok: 3: a_nonexistent_command: not found
ls: cannot access 'a_nonexistent_file': No such file or directory
This line is printed despite any previous error.

答案1

那是因为.是一个特殊的内置函数。这是 POSIX 的要求。大多数其他 POSIX shell 也会执行此操作,但仅在bash它们zsh处于各自的 posix/sh/ksh 模式时(包括当它们被调用时sh)。

要消除特殊内置函数的特殊性,POSIX 方法是在其前面加上前缀command

$ dash -c 'command . /x; echo here'
dash: 1: .: cannot open /x: No such file
here

exit如果源文件在解释其内容时调用或遇到致命错误,这不会阻止 shell 退出。在破折号的情况下(与bash -o posix例如相反),源文件中的特殊内置函数失败不会导致 shell 退出,尽管它们也以command.

请注意,如果errexit还启用了该选项,则如果无法打开源文件,shell 仍将退出,但这一次是因为command返回非零退出状态:

$ dash -o errexit -c 'command . /x; echo "$? here"'
dash: 1: .: cannot open /x: No such file

解决方法是处理该错误:

$ dash -o errexit -c 'command . /x || echo "that is fine"; echo "$? here"'
dash: 1: .: cannot open /x: No such file
that is fine
0 here

或者:

$ dash -o errexit -c 'command . /x && : non-fatal; echo "$? here"'
dash: 1: .: cannot open /x: No such file
2 here

(或者调用//语句command .的条件部分)。ifwhileuntil

在这种情况下dash(至少是当前版本,与bash -o posix示例相反),这不会取消errexit对源文件中代码求值的效果:

$ dash -ec 'command . ./file || echo fine; echo here'
before
$ bash -o posix -ec 'command . ./file || echo fine; echo x'
before
after
x

errexit除了最简单的脚本之外,最好避免众多原因之一。

请注意,zsh 的command出现早于 POSIX,并且具有不同的语义。command .如果不在 sh 仿真/POSIX 模式下,则不能在那里使用。

为了避免该错误,您可以在丢弃 stderr 的同时在某些 fd 上打开文件并获取相应的文件,而不是尝试事先进行 TOCTOU race 附带的检查/dev/fd/n

$ dash -c '{ command . /dev/fd/3; } 4>&2 2> /dev/null 3< file 2>&4 4>&-; echo here'
here

当重定向失败时,重定向的命令(这里是指挥组) 不会被执行,因此 shell 不会尝试执行该特殊的内置函数,从而避免退出 shell 的副作用。我们仍然添加一个command前缀,以防操作系统是 Linux 或 Cygwin,其中打开方式/dev/fd/x不像 a,dup(x)并且如果文件的权限自在 fd 3 上打开以来发生了更改,仍然可能会失败。

答案2

它是POSIX 要求此类失败必须中止 shell:

如果没有找到可读文件,非交互式 shell 将中止

因此,您唯一的选择是通过确保文件存在并且在需要时可读来保护命令;

[ -f file ] && [ -r file ] && . ./file

您还可以将其扩展为完整if语句,以便在文件不存在时打印错误或警告消息

相关内容