这个问题已经出现在面试前的测验中,这让我发疯。谁能回答这个问题并让我放心?测验没有引用特定的shell,但是工作描述是针对unix sa的。
同样,问题很简单...


'set -e'是什么?这样做,为什么会被认为是危险的?


评论

这是一个相关的线程:stackoverflow.com/questions/64786/error-handling-in-bash / ...

#1 楼

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

访问者可能正在寻找的答案是:


在创建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脚本,并且使用int -e调用常见的init.d函数库并不安全。对于init.d脚本,通常不使用set -e而不是分别检查每个命令的结果通常会更容易。


从访问者的角度来看,这是一个有效的问题,因为它可以衡量应聘者具有服务器级脚本和自动化的工作知识

评论


好点子。从技术上讲,这可能会导致启动过程暂停,但从技术上来说这可能是错误的,但不应导致整个脚本停止运行

–马特
2012年9月19日下午2:29

尽管我同意stackoverflow.com/questions/78497/…,但我认为这种样式非常适合bash用于短期init检查和日志记录或其他环境猴子修补,然后再执行目标工作负载。我们对docker镜像使用相同的策略初始化脚本。

– joshperry
17年6月6日在3:52

#2 楼

来自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.


很不幸,我没有足够的创意去思考为什么它会很危险,除了“脚本的其余部分不会执行”之外或“它可能可能掩盖了实际问题”。

评论


掩盖真实/其他问题,是的,我想我可以看到...我也在努力举一个例子说明它很危险。

–cpbills
2010年5月19日在16:48

有设置的手册页!我总是以buildin(1)结尾。谢谢。

–Jared Beck
13年5月29日在23:27

我怎么知道set的文档将在man 1 bash中找到?以及如何在如此巨大的手册页中为set关键字找到-e选项?你不能/ -e在这里搜索

– phil294
17-6-5在6:33



@Blauhirn使用帮助集

– phil294
18年3月13日在22:04

#3 楼

应当注意,可以为脚本的各个部分打开和关闭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


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


如果失败的命令立即是
命令列表的一部分,则不执行
ERR陷阱。在一段时间或直到关键字之后,
if语句中的部分测试,在&&或⎪⎪列表中执行的部分命令
,或者该命令的返回值被反转
通过!这些是
errexit选项所遵循的相同条件。


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

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

另请参见BashFAQ / 105为什么不起作用设置-e(或设置-o errexit或陷阱ERR)是否达到了我的期望?

评论


虽然稍微偏离了这个问题,但这个答案正是我要寻找的:只在脚本的一小部分将其关闭!

–斯蒂芬·比杰斯特(Stephan Bijzitter)
17年11月9日在10:18

#4 楼

请记住,这是求职面试的一项测验。这些问题可能是当前员工写的,可能是错误的。这并不一定很糟糕,每个人都会犯错,面试问题通常位于一个黑暗的角落,没有经过审查,只有在面试过程中才会出现。没有什么我们认为“危险的”。但是该问题的作者可能会错误地认为“ set -e”是危险的,因为他们自己的无知或偏见。也许他们写了一个有问题的shell脚本,遭到了轰炸,然后他们错误地以为“ set -e”应归咎于他们,而实际上他们却忽略了编写适当的错误检查。

我参加了在过去的两年中,可能有40次面试,而面试官有时会提出错误的问题或有错误的答案。

或者这是一个棘手的问题,虽然很la脚,但并不完全出乎意料。

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

评论


+1我在同一条船上,而我一直在进行的许多“技术”面试至少被误导了。

–cpbills
2010年5月19日在17:43

哇。在debian列表上很好找到。 +1是对面试问题的无知。我记得自从100%确信自己是对的以来就曾经争论过一次netback答案。他们说不。我回家搜索了一下,我是对的。

–egorgry
2010年5月19日在18:48

#5 楼

set -e告诉bash在脚本中,只要任何东西返回非零的返回值就退出。权限,并且在再次限制它们之前,脚本已死。

评论


那很有意思。我没有考虑过在过早终止的脚本中打开权限,但是我可以看到这被认为是危险的。这个测验的有趣之处在于,您不能使用任何参考资料,例如man或google;如果不能完全回答,则根本不要回答。

–egorgry
2010年5月19日17:02

那真是愚蠢,我会重新考虑这个老板...开玩笑(有点)。拥有强大的知识基础是一件好事,但是一半的IT工作是知道/ where /查找信息...希望他们足够聪明,可以在为申请人评分时考虑到这一点。在旁注中,我看到/ no /用于set -e,实际上,对我而言,它懒惰地忽略了脚本中的错误检查...因此,请与该雇主一起记住这一点...如果他们经常使用它,它们的脚本是什么样的,您将不可避免地需要维护...

–cpbills
2010年5月19日17:45

好点@cpbills。在脚本中我也看不到set -e的用处,这可能就是为什么它让我如此沮丧的原因。我想发布所有问题,因为有些问题真的很好,有些却像古怪而随意。

–egorgry
2010年5月19日在18:46

我已经在许多系统Cron工作中看到了...

–安德鲁(Andrew)
2010年5月20日,0:29

#6 楼

如果遇到非零退出代码,则在某些情况下,set -e将终止脚本。用几句话总结使用它的危险:它不符合人们的想法。

我认为,它应该被视为危险的hack,出于兼容性考虑,它仍然存在仅用于目的。 set -e语句不会将shell从使用错误代码的语言转换为使用类似异常控制流的语言,而只是极力地尝试模仿这种行为。

Greg Wooledge有很多话要说。 set -e的危险:



https://www.mail-archive.com/bug-bash@gnu.org/msg19497.html(最近的邮件列表帖子有一个有趣的类比)

http://mywiki.wooledge.org/BashFAQ/105(有关set -e陷阱的Wiki页面)

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

set -e的非直觉行为的一些示例(某些摘自上面的Wiki链接):



set -e
x=0
let x++
echo "x is $x"


上面的内容将导致shell脚本过早退出,因为let x++返回0,let关键字将其视为虚假值并将其转换为非零退出代码。 set -e注意到了这一点,并以静默方式终止了脚本。

尽管唯一的区别在于已将单行重构为函数,但上述内容将在/opt/foo不存在的情况下终止。这是因为它最初起作用的事实是/opt/foo行为的特殊例外。当set -e返回非零值时,它将被a && b忽略。但是,既然它是一个函数,则该函数的退出代码等于该命令的退出代码,并且返回非零的函数将以静默方式终止脚本。


set -e
[ -d /opt/foo ] && echo "Warning: foo is already installed. Will overwrite." >&2
echo "Installing foo..."


上面的内容将从文件set -e中读取数组config_vars。正如作者可能打算的那样,如果缺少config,它将以错误终止。正如作者可能不希望的那样,如果config没有以换行符结尾,则它默默终止。如果此处未使用config,则set -e将包含该文件的所有行,无论该文件是否以换行符结尾。请注意Sublime Text的用户(以及其他错误处理换行符的文本编辑器)。


set -e
check_previous_install() {
    [ -d /opt/foo ] && echo "Warning: foo is already installed. Will overwrite." >&2
}

check_previous_install
echo "Installing foo..."


作者可能会合理地期望,如果由于某种原因,用户config_vars不存在,则$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++ -condition的要点。
shell终止的位置在shell或shell版本之间不一致。由于if的行为在这些版本之间进行了调整,因此可能意外地编写了在旧版本的bash上表现不同的脚本。它周围的问题建议您反对它,而其他一些人则建议在它主动知道陷阱时小心谨慎。有许多shell脚本新手在所有脚本上推荐set -e作为错误条件的综合解决方案,但在现实生活中,这种方式行不通。

set -e不能代替教育。

#7 楼

我说这很危险,因为您不再控制他的脚本流了。只要脚本调用的任何命令返回非零值,脚本就可以终止。因此,您要做的就是做一些改变任何组件的行为或输出的操作,然后终止主脚本。这可能更多是样式问题,但肯定会带来后果。如果您的主脚本应该设置一些标志,但不是因为它提前终止,该怎么办?如果它假定该标志应该存在,或者使用意外的默认值或旧值,您最终都会对系统的其余部分造成错误。

评论


完全同意这个答案。这并不是天生的危险,但是这是一个意外退出的机会。

– pboin
2010年5月20日,0:18

这让我想起了我的C ++教授,因为我在程序中没有单个入口点/单个出口点,所以总是从我的分配中提取点。我以为这纯粹是“原则”,但是这套e生意肯定演示了事情如何以及为什么会完全失控。最终,程序是关于控制和确定性的,如果提前终止,您将同时放弃这两种方法。

–马辛
2010年5月20日上午11:45

#8 楼

作为@Marcin回答的一个具体示例,这个问题已经使我深深地咬住了我,想像一下您的脚本中某处有rm foo/bar.txt行。如果foo/bar.txt实际上不存在,通常没什么大不了的。但是现在有了set -e,您的脚本将在那里提前终止!糟糕。