'set -e' 有什么作用,为什么它会被认为是危险的?

ego*_*gry 157 linux unix shell

这个问题出现在面试前的测验中,这让我很抓狂。任何人都可以回答这个问题,让我放心吗?测验没有提到特定的 shell,但工作描述是针对 unix sa 的。问题再次很简单......

'set -e' 有什么作用,为什么它会被认为是危险的?

小智 167

set -e 如果任何子命令或管道返回非零状态,则导致 shell 退出。

面试官想要的答案可能是:

创建 init.d 脚本时使用“set -e”会很危险:

来自http://www.debian.org/doc/debian-policy/ch-opersys.html 9.3.2 --

在 init.d 脚本中使用 set -e 时要小心。编写正确的 init.d 脚本需要在守护进程已经运行或已经停止时接受各种错误退出状态而不中止 init.d 脚本,并且在 set -e 生效的情况下调用常见的 init.d 函数库是不安全的。对于 init.d 脚本,不使用 set -e 而是分别检查每个命令的结果通常更容易。

从面试官的角度来看,这是一个有效的问题,因为它衡量了候选人在服务器级脚本和自动化方面的工作知识


Ign*_*ams 35

来自bash(1)

          -e      Exit immediately if a pipeline (which may consist  of  a
                  single  simple command),  a subshell command enclosed in
                  parentheses, or one of the commands executed as part  of
                  a  command  list  enclosed  by braces (see SHELL GRAMMAR
                  above) exits with a non-zero status.  The shell does not
                  exit  if  the  command that fails is part of the command
                  list immediately following a  while  or  until  keyword,
                  part  of  the  test  following  the  if or elif reserved
                  words, part of any command executed in a && or  ??  list
                  except  the  command  following  the final && or ??, any
                  command in a pipeline but the last, or if the  command’s
                  return  value  is being inverted with !.  A trap on ERR,
                  if set, is executed before the shell exits.  This option
                  applies to the shell environment and each subshell envi-
                  ronment separately (see  COMMAND  EXECUTION  ENVIRONMENT
                  above), and may cause subshells to exit before executing
                  all the commands in the subshell.
Run Code Online (Sandbox Code Playgroud)

不幸的是,我没有足够的创造力去思考为什么它会很危险,除了“脚本的其余部分不会被执行”或“它可能会掩盖真正的问题”。


Den*_*son 22

应该注意的是,set -e可以为脚本的各个部分打开和关闭。它不必为整个脚本的执行打开。它甚至可以有条件地启用。也就是说,我从来没有使用过它,因为我自己做错误处理(或不做)。

some code
set -e     # same as set -o errexit
more code  # exit on non-zero status
set +e     # same as set +o errexit
more code  # no exit on non-zero status
Run Code Online (Sandbox Code Playgroud)

同样值得注意的是该trap命令的 Bash 手册页部分,该部分还描述了set -e在某些情况下的功能。

如果失败的命令是紧跟在 while 或 until 关键字之后的命令列表的一部分、if 语句中测试的一部分、在 && 或 ?? 中执行的命令的一部分,则不会执行 ERR 陷阱。列表,或者命令的返回值是否通过 ! 反转。这些是 errexit 选项所遵循的相同条件。

因此,在某些情况下,非零状态不会导致退出。

我认为危险在于不了解何时set -e起作用,何时不起作用,并且在某些无效假设下错误地依赖它。

另请参阅BashFAQ/105 为什么设置 -e(或设置 -o errexit,或陷阱 ERR)不按我的预期执行?


Ste*_*ski 14

请记住,这是求职面试的测验。问题可能是现任工作人员写的,也可能是错的。这不一定是坏事,每个人都会犯错,而且面试问题经常坐在一个黑暗的角落里,没有审查,只有在面试时才会出现。

'set -e' 完全有可能不会做任何我们认为“危险”的事情。但该问题的作者可能会错误地认为 'set -e' 是危险的,因为他们自己的无知或偏见。也许他们写了一个有缺陷的 shell 脚本,它被炸得很厉害,他们错误地认为 'set -e' 是罪魁祸首,而实际上他们忽略了编写正确的错误检查。

在过去的 2 年里,我参加了大约 40 个工作面试,面试官有时会问错误的问题,或者给出错误的答案。

或者也许这是一个棘手的问题,这会很蹩脚,但并非完全出乎意料。

或者这是一个很好的解释:http : //www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg473314.html


Sco*_*der 11

set -e如果遇到非零退出代码,则终止脚本,但在某些情况下除外。用几句话总结它使用的危险:它的行为不像人们认为的那样。

在我看来,它应该被视为一种危险的黑客攻击,它仅出于兼容性目的而继续存在。该set -e语句并没有将 shell 从使用错误代码的语言转变为使用类似异常的控制流的语言,它只是试图模拟这种行为。

格雷格·伍利奇 (Greg Wooledge) 对以下危险有很多话要说set -e

在第二个链接中,有各种不直观和不可预测行为的例子set -e

一些不直观行为的例子set -e(一些取自上面的维基链接):

  • 设置 -e
    x=0
    让 x++
    echo "x 是 $x"
    以上会导致shell脚本过早退出,因为let x++返回0,这被let关键字视为假值并变成非零退出代码。set -e注意到这一点,并默默地终止脚本。
  • 设置 -e
    [ -d /opt/foo ] && echo "警告:foo 已安装。将覆盖。" >&2
    echo "正在安装 foo..."
    

    以上按预期工作,如果/opt/foo已经存在则打印警告。

    设置 -e
    check_previous_install() {
        [ -d /opt/foo ] && echo "警告:foo 已安装。将覆盖。" >&2
    }
    
    check_previous_install
    echo "正在安装 foo..."
    

    上面,尽管唯一的区别是单行已被重构为一个函数,但如果/opt/foo不存在则将终止。这是因为它最初工作的事实是set -e的行为的特殊例外。当a && b返回非零值时,它会被 忽略set -e。但是,现在它是一个函数,函数的退出代码等于该命令的退出代码,返回非零的函数将默默地终止脚本。

  • 设置 -e
    IFS=$'\n' read -d '' -r -a config_vars < config
    

    以上将从config_vars文件中读取数组config。正如作者可能打算的那样,如果config缺少,它会以错误终止。由于作者可能不打算,如果config不以换行符结尾,它会默默地终止。如果set -e此处未使用,则config_vars无论是否以换行符结尾,都将包含文件的所有行。

    Sublime Text(以及其他不正确处理换行符的文本编辑器)的用户请注意。

  • 设置 -e
    
    should_audit_user() {
        本地组groups="$(groups "$1")"
        对于 $groups 中的组;做
            如果 [ "$group" = 审计]; 然后返回0;菲
        完毕
        返回 1
    }
    
    if should_audit_user "$user"; 然后
        记录器“废话”
    菲
    

    这里的作者可能会合理地期望,如果由于某种原因用户$user不存在,那么groups命令将失败并且脚本将终止,而不是让用户执行一些未经审计的任务。但是,在这种情况下,set -e终止永远不会生效。如果$user由于某种原因无法找到,则该should_audit_user函数不会终止脚本,而是返回错误的数据,就好像set -e没有生效一样。

    这适用于从if语句的条件部分调用的任何函数,无论嵌套有多深,无论它在哪里定义,即使您set -e再次在其中运行。if在任何时候使用都会完全禁用 的效果,set -e直到条件块完全执行为止。如果作者没有意识到这个陷阱,或者不知道在所有可能调用函数的情况下他们的整个调用堆栈,那么他们将编写错误的代码,并且提供的虚假安全感set -e将至少部分地责备。

    即使作者完全意识到这个陷阱,解决方法是用与没有 的相同方式编写代码set -e,有效地使该开关没有用处;作者不仅必须编写手动错误处理代码,好像set -e没有生效一样,而且 的存在set -e可能使他们误以为他们不必这样做。

一些进一步的缺点set -e

  • 它鼓励草率的代码。完全忘记了错误处理程序,希望任何失败的都会以某种合理的方式报告错误。但是,对于let x++上述示例,情况并非如此。如果脚本意外终止,它通常是静默的,这会妨碍调试。如果脚本没有消亡并且您预期它会死(请参阅上一个要点),那么您手上就有了一个更微妙和更阴险的错误。
  • 它会让人们产生一种虚假的安全感。再次查看if-condition 要点。
  • shell 终止的位置在 shell 或 shell 版本之间不一致。由于set -e在这些版本之间进行了调整,因此可能会意外编写一个脚本,该脚本在旧版本的 bash 上表现不同。

set -e是一个有争议的问题,一些意识到围绕它的问题的人建议反对它,而另一些人则建议在积极了解陷阱的同时小心谨慎。有许多 shell 脚本新手推荐set -e所有脚本作为错误条件的包罗万象,但在现实生活中它不是这样工作的。

set -e 不能代替教育。


cpb*_*lls 9

set -e 在脚本中告诉 bash 在任何返回非零返回值时退出。

我可以看到这会很烦人,而且有问题,不确定是否危险,除非你已经打开了某些东西的权限,并且在你再次限制它们之前,你的脚本已经死了。

  • 那太愚蠢了,我会重新考虑这个雇主......开玩笑(有点)。拥有扎实的知识基础固然很好,但一半的 IT 工作是知道/在哪里/可以找到信息……希望他们足够聪明,能够在为申请人评分时考虑到这一点。附带说明一下,我看到 /no/ 用于 `set -e`,事实上,对我来说,它说的是懒惰忽略脚本中的错误检查......所以请记住这一点,这个雇主也是...... .如果他们经常使用它,他们的脚本是什么样子的,你将不可避免地需要维护...... (6认同)