使用(...)创建的子shell与bash -c'...'的行为不同

时间:2015-04-29 13:55:15

标签: bash shell unix

我在命令行中使用set -e (...)但在执行false命令时它不会退出子shell。

代码1:

( set -e; echo "$BASHPID: start"; false; echo "foobar"; date; ) && 
  echo "$BASHPID: ok" || echo "$BASHPID: nope"
9136: start
foobar
Wed, Apr 29, 2015  7:14:24 PM
7292: ok

但是,如果我使用bash -c创建子shell,那么它的行为与我期望的一样。

代码2:

bash -c 'set -e; echo "$BASHPID: start"; false; echo "foobar"; date;' &&
  echo "$BASHPID: ok" || echo "$BASHPID: nope"
7880: start
7292: nope

有趣的是,如果我在子shell后删除&&||部分,那么(...)也会表现良好。

代码3:

( set -e; echo "$BASHPID: start"; false; echo "foobar"; date; )
5940: start

我得到的结论是:

  1. (...)的行为与bash -c
  2. 不同
  3. (...) && false(...)的行为不同,也会更改子shell的行为。
  4. 我在解释这种奇怪的(至少对我而言)行为时犯了一些明显的错误吗?

1 个答案:

答案 0 :(得分:2)

set -e&&的行为是故意的。

当命令是&&序列的一部分时,它明确地不会触发。

来自POSIX规范:

  

<强> -e

     

当此选项打开时,当任何命令失败时(由于Shell错误的后果中列出的任何原因或返回大于零的退出状态),shell将立即退出,但出现以下异常:   多命令管道中任何单个命令的失败都不会导致shell退出。只考虑管道本身的故障。

     

在执行while,until,if或elif保留字之后的复合列表时,应忽略-e设置,以!开头的管道!保留字,或除最后一个之外的AND-OR列表的任何命令。

     

如果在忽略-e时,除了subshel​​l命令之外的复合命令的退出状态是失败的结果,则-e不应用于此命令。

     

此要求分别适用于shell环境和每个子shell环境。例如,在:

set -e; (false; echo one) | cat; echo two
     

false命令导致子shell退出而不执行echo one;但是,echo 2是因为管道的退出状态(false; echo one)执行的猫是零。

我认为这里的区别在于shell是否知道这一事实。

(...)大概是因为当前shell执行它而bash -c可能不会因为它是一个完全外部的过程(或其他东西)。

最后一点是猜测,在我看来,这里的子shell行为完全没有用,使得set -e不可靠(这就是许多人建议避免它的原因)。

虽然看起来这可能也提供了一种逃生舱口&#34;对于那个可靠性问题,如果显式运行的shell没有脚本组合的问题。