问题描述
由于当前未知的原因,我们的其中一个bash脚本在运行以下简单行时会产生“无法分叉”错误:
myvar=`mycmd || echo "error"; exit 2`
显然,问题在于无法创建(派生)新进程,因此该命令失败。
但是bash只会忽略该错误并继续在脚本中运行,从而导致意外的问题。
如您所见,我们已经检查了命令本身是否存在错误,但是甚至在命令运行之前就出现了“ Cannot fork”错误。
有没有办法捕获该错误并阻止bash执行?
解决方法
此错误检查实际上存在几个问题,这将阻止它正确处理任何错误,而不仅仅是“无法分叉”错误。
第一个问题是||
的优先级高于;
,因此mycmd || echo "error"; exit 2
仅在echo "error"
失败的情况下才能运行mycmd
,但它将运行exit 2
无条件,无论mycmd
是成功还是失败。
为了解决此问题,应将错误处理命令与{ }
组合在一起。类似于:mycmd || { echo "error"; exit 2; }
。请注意,必须在;
之前插入}
或换行符,否则}
将被视为exit
的参数。
(顺便说一句,有时我会简称为mycmd || echo "error" && exit 2
。请不要这样做。如果echo
由于某种奇怪的原因而失败,则它将不会运行exit
。)
所有这些echo
和exit
,都在反引号创建的子外壳中运行(或者如果该子外壳已成功分叉,则将在子外壳中运行)。这意味着错误消息将保存在myvar
中,而不是打印出来(错误消息通常应发送到标准错误,例如echo "error" >&2
);更重要的是,将退出的子外壳,而不是运行脚本的外壳。主脚本将注意到该子shell退出并出现错误...,并且一直保持运行状态。 (好吧,除非您设置了-e
,但这是whole other ball of potential bugs。)
解决方案是将||
东西放在 后的反引号(或`$(,因为通常比反引号更可取))。这样,它就发生在主外壳中,即打印出错误,即出现错误时退出,等等。这也应解决“无法分叉”的问题,尽管我尚未对其进行测试。
因此,通过所有这些更正,它应该看起来像这样:
myvar=$(mycmd) || {
echo "error" >&2
exit 2
}
哦,正如查尔斯·达菲(Charles Duffy)在评论中指出的那样,如果您使用local myvar=$(mycmd)
或export myvar=$(mycmd)
之类的内容,则local
/ export
/任何命令都将覆盖从mycmd
退出状态。如果需要这样做,请将变量的属性与其值分开设置:
local myvar
myvar=$(mycmd) || {
...