在子shell中设置-e

PSk*_*cik 11 shell subshell

我认为set -e对子外壳和顶级外壳有相同的影响。显然,事实并非如此。这个:

(
  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
Run Code Online (Sandbox Code Playgroud)

印刷

false2
false3
false5
Run Code Online (Sandbox Code Playgroud)

这是在哪里记录的?我可以让子外壳程序在错误时终止,而不用&&(或|| exit $?在每个命令之后不执行)连接所有命令吗?

编辑:

我的特定用例是这样的:

set -e
  # ...
status=0
( false; true ) || status=$?
report_code $status
return $status
Run Code Online (Sandbox Code Playgroud)

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

我用以下方法解决了它:

set -e
  # ...
set +e
( false; true ); status=$?
set -e
report_code $status
return $status
Run Code Online (Sandbox Code Playgroud)

我希望我不必这样做,但似乎所有常见的 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
Run Code Online (Sandbox Code Playgroud)

输出:

FORK'D:
dash    0
bash    0
ksh 0
zsh 0
EXEC'D:
dash    1
bash    1
ksh 1
zsh 1
Run Code Online (Sandbox Code Playgroud)

Joh*_*024 17

观察:

$ ( set -e; false ; true ) || echo false1
$ ( set -e; false ; true ) ; echo code=$?
code=1
Run Code Online (Sandbox Code Playgroud)

还:

$ ( set -e; false ; true; echo inside=$? ) || echo false1
inside=0
Run Code Online (Sandbox Code Playgroud)

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

的哲学set -e通常是它仅在未捕获的错误时退出。在这里,||子外壳外部的存在似乎告诉外壳子外壳内部的错误被“捕获”,因此set -e不会在false.

set -e有许多令人惊讶的行为。请参阅“为什么 set -e 不符合我的预期?”

文档

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

-e

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