zan*_*etu 7 shell dash shell-script
我有一个破折号 shell 脚本,旨在运行到文件末尾:
#!/usr/bin/dash
# File b_shell
. a_nonexistent_file
echo "I want to print this line despite any previous error."
Run Code Online (Sandbox Code Playgroud)
但是,它在无法获取不存在的文件后停止运行:
$ ./b_shell
./b_shell: 3: .: a_nonexistent_file: not found
Run Code Online (Sandbox Code Playgroud)
我尝试过包括set -e
和在内的候选解决方案 || true
,这些解决方案可以在互联网上轻松找到,但无济于事。
附加说明:
#!/usr/bin/dash
# File b_shell_ok
a_nonexistent_command
ls a_nonexistent_file
echo "This line is printed despite any previous error."
Run Code Online (Sandbox Code Playgroud)
$ ./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.
Run Code Online (Sandbox Code Playgroud)
那是因为.
是一个特殊的内置函数。这是 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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
解决方法是处理该错误:
$ 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
Run Code Online (Sandbox Code Playgroud)
或者:
$ dash -o errexit -c 'command . /x && : non-fatal; echo "$? here"'
dash: 1: .: cannot open /x: No such file
2 here
Run Code Online (Sandbox Code Playgroud)
(或者调用//语句command .
的条件部分)。if
while
until
在这种情况下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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
当重定向失败时,重定向的命令(此处为命令组)不会执行,因此 shell 不会尝试执行该特殊的内置命令,从而避免退出 shell 的副作用。我们仍然添加一个command
前缀,以防操作系统是 Linux 或 Cygwin,其中打开方式/dev/fd/x
不像 a dup(x)
,并且如果文件的权限自在 fd 3 上打开以来发生了更改,仍然可能会失败。
POSIX 要求此类失败必须中止 shell:
如果没有找到可读文件,非交互式 shell 将中止
因此,您唯一的选择是通过确保文件存在并且在需要时可读来保护命令;
[ -f file ] && [ -r file ] && . ./file
Run Code Online (Sandbox Code Playgroud)
您还可以将其扩展为完整if
语句,以便在文件不存在时打印错误或警告消息
归档时间: |
|
查看次数: |
1392 次 |
最近记录: |