在子 shell 中设置 -e

在子 shell 中设置 -e

我认为set -e对子 shell 的影响与对顶级 shell 的影响相同。显然,事实并非如此。这:

(
  set -e
  false 
  true
) || echo false1


bash -ec '
  set -e
  false 
  true
' || echo false2


bash <<EOF || echo false3
  set -e
  false 
  true
EOF

bash <<EOF || echo false4
  false 
  true
EOF

bash <<EOF || echo false5
  false  &&
  true
EOF

印刷

false2
false3
false5

这是在哪里记录的?我是否可以让子 shell 因错误而终止,而无需连接所有命令&&(或在每个命令后不执行操作|| exit $?)?

编辑:

我的具体用例是这样的:

set -e
  # ...
status=0
( false; true ) || status=$?
report_code $status
return $status

子 shell 的内容是我的实际代码。这样做的问题是它总是将状态设置为 0,并且由于外部设置 -e而替换|| 为会导致不必要的错误退出。;

我用以下方法解决了它:

set -e
  # ...
set +e
( false; true ); status=$?
set -e
report_code $status
return $status

我希望我不必这样做,但似乎所有常见的 shell 都显示了 execed-subshel​​l 与 just-forked-subshel​​l 二分法:

#!/bin/sh

echo FORK\'D:
export SH
for SH in dash bash ksh zsh; do
    $SH -c 'st=0; ( set -e; false; true ) || st=$?; printf "%s\t%s\n" $SH  $st; '
done

echo EXEC\'D:
for SH in dash bash ksh zsh; do
    $SH -c 'st=0; '$SH' -c " set -e; false; true " || st=$?; printf "%s\t%s\n" $SH $st; '
done

输出:

FORK'D:
dash    0
bash    0
ksh 0
zsh 0
EXEC'D:
dash    1
bash    1
ksh 1
zsh 1

答案1

观察:

$ ( set -e; false ; true ) || echo false1
$ ( set -e; false ; true ) ; echo code=$?
code=1

还:

$ ( set -e; false ; true; echo inside=$? ) || echo false1
inside=0

显然,当子 shell 后跟||,时set -e,不会导致子 shell 在到达false命令时退出。相反,子 shell 继续并执行true(and echo inside=$?)。

它的理念set -e通常是,它仅在未捕获的错误时才存在。在这里,子 shell 外部的存在||似乎告诉 shell 子 shell 内部的错误已被“捕获”,因此set -e不会导致 后退出false

set -e有许多令人惊讶的行为。看“为什么 set -e 没有达到我的预期?”

文档

上述行为在以下文档中有所暗示man bash

-e

如果管道(可能由单个简单命令组成)、列表或复合命令(请参阅上面的 SHELL GRAMMAR)以非零状态退出,则立即退出。 如果失败的命令是 shell,则 shell 不会退出命令列表的一部分紧跟在 while 或 Until 关键字之后,测试的一部分在 if 或 elif 保留字之后,在 && 或 || 中执行的任何命令的一部分列表除了最后一个 && 或 || 后面的命令之外,管道中除最后一个命令之外的任何命令,或者命令的返回值与 ! 反转。如果子 shell 之外的复合命令由于在忽略 -e 时命令失败而返回非零状态,则 shell 不会退出。如果设置了 ERR 陷阱,则会在 shell 退出之前执行。此选项分别适用于 shell 环境和每个子 shell 环境(请参阅上面的命令执行环境),并且可能会导致子 shell 在执行子 shell 中的所有命令之前退出。 [强调。]

相关内容