这个问题已经出现在面试前的测验,这让我疯狂。 任何人都可以回答这个问题,让我安心吗? 测验没有提到特定的shell,但是工作描述是针对unix sa的。 再次,问题是简单的…
“set -e”是做什么的,为什么它会被认为是危险的?
set -e
会导致shell在任何子命令或pipe道返回非零状态时退出。
面试官可能要找的答案是:
创buildinit.d脚本时使用“set -e”会很危险:
From http://www.debian.org/doc/debian-policy/ch-opersys.html 9.3.2 –
在init.d脚本中小心使用set -e。 编写正确的init.d脚本需要在守护进程已经运行或已经停止而不中止init.d脚本的情况下接受各种错误退出状态,并且通常的init.d函数库不安全,因此无法使用set -e进行调用。 对于init.d脚本,不使用set -e会更容易,而是分别检查每个命令的结果。
从面试官的angular度来看,这是一个有效的问题,因为它衡量了候选人在服务器级脚本和自动化方面的工作知识
从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.
不幸的是,我没有足够的创造力去思考为什么它会是危险的,除了“脚本的其余部分不会被执行”或“它可能可能掩盖真正的问题”。
请记住,这是一个面试的测验。 这些问题可能是现在的员工写的,可能是错的。 这不一定是坏事,每个人都会犯错误,面试问题经常坐在黑暗的angular落里,没有经过审查,只能在面试时出来。
“set -e”完全可以做任何我们认为是“危险”的事情。 但是,这个问题的作者可能会错误地认为,由于自己的无知或偏见,“定”是危险的。 也许他们写了一个错误的shell脚本,它轰炸得可怕,而且他们错误地认为“set -e”是应该的,实际上他们忽略了正确的错误检查。
过去两年我参加了大概40次求职面试,面试官有时会提出错误的问题,或者有错误的答案。
或者这可能是个狡猾的问题,这可能是蹩脚的,但并不完全出乎意料。
或者,也许这是一个很好的解释: http : //www.mail-archive.com/[email protected]/msg473314.html
应该注意的是, set -e
可以打开和closures脚本的各个部分。 对于整个脚本的执行,不一定非要执行。 它甚至可以有条件启用。 这就是说,我从来没有使用它,因为我做我自己的error handling(或不)。
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
function。
如果失败的命令是紧接着一段时间或直到关键字,在if语句中的testing的一部分,在&&或列表中执行的命令的一部分,或者命令的返回值正在通过!倒置。 这些都是errexit选项所遵循的相同条件。
所以在某些情况下,非零状态不会导致退出。
我认为危险在于set -e
进入时,不知道什么时候不了解,在某种无效的假设下错误地依赖。
请参阅BashFAQ / 105为什么没有设置-e(或设置-er errexit,或陷阱ERR)做我的预期?
set -e
在脚本中告诉bash,当任何东西返回一个非零的返回值时退出。
我可以看到那将会是多么恼人,而且越野车,不知道危险,除非你打开了某些东西的权限,而在你再次限制它们之前,脚本就死掉了。
我会说这是危险的,因为你不再控制脚本的stream动。 只要脚本调用的任何命令返回非零值,脚本就可以终止。 所以你所要做的就是做一些改变任何组件的行为或输出的东西,并且你可以终止主脚本。 这可能是更多的风格问题,但它肯定有后果。 如果你的主要脚本应该设置一些标志,而不是因为它提前终止? 如果假设该标志应该在那里,或者使用意外的默认值或旧值,那么最终会导致系统的其余部分发生故障。
set -e
在遇到非零退出代码时终止脚本,除了在某些情况下。 用几句话总结一下它的用法的危害:它并不像人们想象的那样行事。
在我看来,它应该被认为是一个危险的黑客,它只是为了兼容性而继续存在。 set -e
语句不会将shell从使用错误代码的语言转换为使用类似exception的控制stream的语言,而只是很less尝试模拟该行为。
Greg Wooledge在set -e
的危险方面有很多话要说:
set -e
的陷阱的维基页面) 在第二个链接中,存在set -e
的不直观和不可预知行为的各种示例。
set -e
一些非直观行为的示例(一些来自上面的wiki链接):
设置-e x = 0的 让x ++ 回声“x是$ x”
以上操作将导致shell脚本过早退出,因为let x++
返回0,将let
关键字视为falsy值,并将其转换为非零退出代码。 set -e
注意到这一点,并静静地终止脚本。
设置-e [-d / opt / foo] && echo“警告:foo已经安装,请覆盖。” >&2 回声“安装foo ...”
上面的工作如预期的那样,如果/opt/foo
已经存在,则打印警告。
设置-e check_previous_install(){ [-d / opt / foo] && echo“警告:foo已经安装,请覆盖。” >&2 } check_previous_install 回声“安装foo ...”
上面,尽pipe唯一的区别是一行被重构成一个函数,如果/opt/foo
不存在,将终止。 这是因为它最初起作用的事实是set -e
行为的一个特例。 当a && b
返回非零时,它会被set -e
忽略。 但是,现在它是一个函数,函数的退出代码等于该命令的退出代码,返回非零的函数将静默终止脚本。
设置-e IFS = $'\ n'读-d''-r -a config_vars <config
以上将从文件config
读取数组config_vars
。 正如作者可能打算的那样,如果缺lessconfig
,它将终止一个错误。 由于作者可能不打算,如果config
不以换行符结束,它会默默终止。 如果在这里不使用set -e
,那么config_vars
将包含文件的所有行, config_vars
是否以换行符结束。
崇高文本的用户(和其他文本编辑器错误地处理新行),请注意。
设置-e should_audit_user(){ 本地群组=“$(groups”$ 1“)” 为$组的团体; 做 如果[“$ group”= audit]; 然后返回0; 科幻 DONE 返回1 } 如果should_audit_user“$ user”; 然后 logging器'Blah' 科幻
这里的作者可能会合理地预期,如果由于某种原因用户$user
不存在,那么groups
命令将失败,脚本将终止而不是让用户执行一些未经审计的任务。 但是,在这种情况下, set -e
终止不会生效。 如果由于某种原因找不到$user
,而不是终止脚本,那么should_audit_user
函数就会返回不正确的数据,就像set -e
没有生效一样。
这适用于从if
语句的条件部分调用的任何函数,无论嵌套的深度如何,无论定义在哪里,即使再次运行set -e
也是如此。 if
在任何时候使用, if
完全禁用set -e
的效果,直到条件块完全执行。 如果作者没有意识到这个缺陷,或者在所有可能调用函数的情况下都不知道他们的整个调用堆栈,那么他们将编写错误的代码, set -e
提供的错误的安全意识将会在至less部分是由于责备。
即使作者完全意识到了这个缺陷,解决方法是编写代码的方式与编写代码相同,不需要set -e
,从而使该开关的效率低于无用; 不仅作者必须编写人工error handling代码,就好像set -e
没有效果一样,但set -e
的存在可能会使他们认为他们不需要这样做。
set -e
另外一些缺点:
let x++
这样的例子,情况并非如此。 如果脚本意外死亡,通常是默默的,这会妨碍debugging。 如果脚本没有死,而且你期望(见前面的要点),那么你的手上就会有一个更微妙和更隐秘的错误。 if
-condition项目符号。 set -e
的行为,可能意外地编写了一个在旧版本的bash上performance不同的脚本。 set -e
是一个有争议的问题,有些人意识到围绕它的问题build议反对它,而另一些人只是build议小心,而它是活跃的知道陷阱。 有许多shell脚本新手谁build议在所有脚本上set -e
作为错误条件的一个捕获所有,但在现实生活中,它不会这样工作。
set -e
不能代替教育。
作为@ Marcin的答案的一个更具体的例子,这个例子已经亲自把我咬了,想象你的脚本中有一个rm foo/bar.txt
行。 通常没有什么大不了的,如果foo/bar.txt
实际上不存在。 但是,现在set -e
现在你的脚本将在那里提前终止! 哎呀。