shell 命令在 || 右侧时失败不存在

shell fails when command to the right of || does not exist

我遇到了一个问题 tar 发生在涉及挂载的 windows docker 卷上的时间戳的情况下(长话短说),并决定尝试 pax。它有效,但我希望脚本在旧机器上像以前一样工作,直到我在所有地方安装 pax 。我的命令如下所示:

tar -czf foo.tar.gz foo || echo "didnt work, trying pax" && echo foo | pax -wz > foo.tar.gz

目的是当 tar 失败时 pax 仅是 运行。但是 运行 在没有 pax 的机器上使用它,我得到了 pax 不存在的意外错误。出乎意料,因为我认为如果前面的命令 return 成功,|| 会丢弃右边的任何内容。将它缩小到一个适当的测试用例我已经得到了这个,用 bashdash:

进行了测试
# as expected, fails trying to run missing command
echo hello && false || echo 'trying different command' && dsadsa
# hello
# trying different command
# bash: dsadsa: command not found

# UNEXPECTED, command is never run but nevertheless fails because it's missing
echo hello && true || echo 'trying different command' && dsadsa
# hello
# bash: dsadsa: command not found

# as expected, does not try run missing command, current solution/workaround
echo hello && true || (echo 'trying different command' && dsadsa)
# hello

为什么会这样?以及如何将子脱壳作为解决方法?

|| discards whatever is to the right

我觉得你的理解有误,左边成功了就执行右边。其余的仍然 运行s 取决于 return 状态。

shell有这个:

echo hello && false || echo 'trying different command' && dsadsa

组命令具有左关联性,&&|| 具有相同的优先级。所以它看起来像这样:

((echo hello && false) || echo 'trying different command') && dsadsa

现在重要的部分来自POSIX shell specification:

Exit Status

The exit status of an OR list shall be the exit status of the last command that is executed in the list.

a || b的退出状态是最后执行的命令的退出状态。因此,如果 return 的退出状态为零,则它是 a 的退出状态。如果 a return 退出状态为非零,则为 b 的退出状态。

所以:

echo hello && true || echo 'trying different command' && dsadsa
  1. echo hello 成功。 (注意:成功是零退出状态。)
  2. 所以&& true被执行了。
  3. echo hello && true 成功。
  4. 所以|| echo 'trying different command'没有执行,因为echo hello && true的退出状态为零
  5. 因此echo hello && true || echo 'trying different command'的退出状态为零,因为echo hello && true的退出状态为零。
  6. 所以&& dsadsa被执行了。

您的“变通办法”是正确的解决方案。要“保存”一些资源,您可以使用 { 大括号,因为您不需要 运行 子 shell.

echo hello && true || { echo 'trying different command' && dsadsa; }

但是我不关心 echos:

的退出状态
{ echo hello; true; } || { echo 'trying different command'; dsadsa; }

无论如何我都会使用 ifs:

if ! tar -czf foo.tar.gz foo; then
    echo "didnt work, trying pax"
    if ! echo foo | pax -wz > foo.tar.gz; then
       echo "RUN! pax failed too"
    fi
fi