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 成功,||
会丢弃右边的任何内容。将它缩小到一个适当的测试用例我已经得到了这个,用 bash
和 dash
:
进行了测试
# 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
echo hello
成功。 (注意:成功是零退出状态。)
- 所以
&& true
被执行了。
echo hello && true
成功。
- 所以
|| echo 'trying different command'
没有执行,因为echo hello && true
的退出状态为零
- 因此
echo hello && true || echo 'trying different command'
的退出状态为零,因为echo hello && true
的退出状态为零。
- 所以
&& dsadsa
被执行了。
您的“变通办法”是正确的解决方案。要“保存”一些资源,您可以使用 {
大括号,因为您不需要 运行 子 shell.
echo hello && true || { echo 'trying different command' && dsadsa; }
但是我不关心 echo
s:
的退出状态
{ echo hello; true; } || { echo 'trying different command'; dsadsa; }
无论如何我都会使用 if
s:
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
我遇到了一个问题 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 成功,||
会丢弃右边的任何内容。将它缩小到一个适当的测试用例我已经得到了这个,用 bash
和 dash
:
# 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
echo hello
成功。 (注意:成功是零退出状态。)- 所以
&& true
被执行了。 echo hello && true
成功。- 所以
|| echo 'trying different command'
没有执行,因为echo hello && true
的退出状态为零 - 因此
echo hello && true || echo 'trying different command'
的退出状态为零,因为echo hello && true
的退出状态为零。 - 所以
&& dsadsa
被执行了。
您的“变通办法”是正确的解决方案。要“保存”一些资源,您可以使用 {
大括号,因为您不需要 运行 子 shell.
echo hello && true || { echo 'trying different command' && dsadsa; }
但是我不关心 echo
s:
{ echo hello; true; } || { echo 'trying different command'; dsadsa; }
无论如何我都会使用 if
s:
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