【问题标题】:shell fails when command to the right of || does not exist [duplicate]当 || 右侧的命令时,shell 失败不存在[重复]
【发布时间】:2019-11-25 14:06:10
【问题描述】:

我遇到了tar 的问题,在涉及已安装 Windows docker 卷上的时间戳的情况下(长话短说),我决定尝试pax。它起作用了,但我希望脚本像以前一样在旧机器上工作,直到我到处安装pax。我的命令如下所示:

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

意图是pax 仅在tar 失败时运行。但是在没有pax 的机器上运行它会出现pax 不存在的意外错误。出乎意料,因为我认为如果前面的命令返回成功,|| 会丢弃右边的任何内容。把它缩小到一个合适的测试用例我已经得到了这个,用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

为什么会这样?以及如何将 sub-shelling 用作解决方法?

【问题讨论】:

  • a && b || c 不等同于if a; then b; else c; fi。如果a b 失败,c 将运行。
  • 一般规则:不要将&&|| 混合在同一个链中(它们的优先级也相同,与其他语言中的布尔值&| 不同)。如果这是您的意思,请使用 if 声明。
  • @chepner 我的理解是它们应该从左到右阅读,因为它们具有相同的优先级。 if-then-else 是个好主意,谢谢
  • @chepner 知道了,应该读作(a && b) || c,而不是a && (b || c)。现在一切都说得通了。
  • 顺便说一句,如果您的唯一目的是分组,请使用a && { b || c; },而不是a && (b || c);这样,当您实际上不需要它时,我们就可以避免 subshel​​l 开销。

标签: bash sh


【解决方案1】:

||丢弃右边的东西

我认为你的理解是错误的,如果左边成功,它会执行右边。其余的仍会根据返回状态运行。

有这个的外壳:

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

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

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

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

退出状态

OR 列表的退出状态应该是列表中执行的最后一个命令的退出状态。

a || b 的退出状态是最后执行的命令的退出状态。因此,如果它以零退出状态返回,则它是 a 的退出状态。如果a 返回非零退出状态,则为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

【讨论】:

  • 关键字是“左结合”。我认为它是右关联的。或者我把左和右混淆了......没关系,我现在明白了:-)
猜你喜欢
  • 1970-01-01
  • 2019-11-10
  • 2016-10-09
  • 1970-01-01
  • 2012-02-17
  • 1970-01-01
  • 1970-01-01
  • 2011-01-14
  • 1970-01-01
相关资源
最近更新 更多