从子shell退出shell脚本

Ern*_*A C 39 shell shell-script exit subshell

考虑这个片段:

stop () {
    echo "${1}" 1>&2
    exit 1
}

func () {
    if false; then
        echo "foo"
    else
        stop "something went wrong"
    fi
}
Run Code Online (Sandbox Code Playgroud)

通常当func被调用时它会导致脚本终止,这是预期的行为。但是,如果它在子shell中执行,例如在

result=`func`
Run Code Online (Sandbox Code Playgroud)

它不会退出脚本。这意味着调用代码每次都必须检查函数的退出状态。有没有办法避免这种情况?这set -e是为了什么?

Sté*_*las 40

例如,您可以决定退出状态 77 表示退出任何级别的子shell,然后执行

set -E
trap '[ "$?" -ne 77 ] || exit 77' ERR

(
  echo here
  (
    echo there
    (
      exit 12 # not 77, exit only this subshell
    )
    echo ici
    exit 77 # exit all subshells
  )
  echo not here
)
echo not here either
Run Code Online (Sandbox Code Playgroud)

set -E与ERRtraps结合使用有点像 的改进版本,set -e因为它允许您定义自己的错误处理。

在zsh中,ERR陷阱是自动继承的,所以你不需要set -E,你也可以将陷阱定义为TRAPERR()函数,通过修改它们$functions[TRAPERR],比如functions[TRAPERR]="echo was here; $functions[TRAPERR]"

  • 需要注意的一件事是,这个陷阱不会处理内插命令,例如`echo "$(exit 77)"`; 脚本将继续,就好像我们写了 `echo ""` (3认同)
  • 有趣的解决方案!显然比“kill $$”更优雅。 (2认同)

der*_*ert 13

您可以kill $$在调用 之前终止原始外壳 ( ) exit,这可能会奏效。但:

  • 对我来说似乎很丑
  • 如果您在那里有第二个子外壳,即在子外壳中使用子外壳,它将中断。

相反,您可以使用Bash FAQ中的几种方法之一来传回值。不幸的是,它们中的大多数都不是很好。您可能只是在每次函数调用后都无法检查错误(-e有很多问题)。要么,要么切换到 Perl。

  • 谢谢。不过,我宁愿切换到 Python。 (6认同)
  • 正如我所写,现在是 2019 年。告诉某人“切换到 Perl”是荒谬的。很抱歉有争议,但是您会告诉对“C”感到沮丧的人切换到 Cobol,这与 IMO 等效吗?正如 Ernest 指出的那样,Python 是一个更好的选择。我的偏好是 Ruby。无论哪种方式,除了 Perl。 (3认同)
  • (我希望阅读答案的每个人都意识到,当然切换到 Python、Ruby、Lua 甚至 NodeJS 也可以;任何有例外的脚本语言。Perl 恰好是我喜欢的语言,并且在 Unix 中拥有悠久的历史正如 polvoazul 指出的那样,系统管理员世界。) (2认同)

Sté*_*nez 10

作为 的替代方案kill $$,您也可以尝试kill 0,它可以在嵌套子shell 的情况下工作(所有调用方和侧进程都将收到信号)……但它仍然是残酷和丑陋的。

  • 这将杀死整个进程组。您可能会遇到不想要的东西(例如,如果您在后台启动了某些东西)。 (5认同)
  • 这不会杀死进程ID 0吗? (2认同)
  • @ErnestAC 参见 kill(2) 联机帮助页,pids ≤0 具有特殊含义。 (2认同)