bash:'set-e'在if表达式中使用时不起作用



看看这个小脚本:

#!/bin/bash
function do_something() {(
set -e
mkdir "/opt/some_folder"                                     # <== returns 1 -> abort?
echo "mkdir returned $?"                                     # <== sets $0 to 0 again
rsync $( readlink -f "${BASH_SOURCE[0]}" ) /opt/some_folder/ # <== returns 23 -> abort?
echo "rsync returned $?"                                     # <== sets $0 to 0 again
)}

# here  every command inside `do_something` will be executed - regardless of errors
echo "run do_something in if-context.."
if ! do_something ; then
echo "running do_something did not work"
fi
# here `do_something` aborts on first error
echo "run do_something standalone.."
do_something
echo $?

我试图按照这里的建议执行(不要错过引入子shell的额外括号),但我没有单独执行函数(在我的情况下是do_something),而是与if表达式一起执行。

现在,当我运行if ! do_something时,set -e命令似乎没有效果。

有人能向我解释一下吗?

这是Bash参考手册中所期望和描述的。

-e

[…]如果失败的命令是紧接在whileuntil关键字之后的命令列表的一部分,是if语句中测试的一部分

[…]

如果复合命令或shell函数在忽略-e的上下文中执行,则在复合命令或函数体中执行的任何命令都不会受到-e设置的影响,即使设置了-e并且命令返回失败状态也是如此。如果复合命令或shell函数在忽略-e的上下文中执行时设置了-e,则在复合命令或包含函数调用的命令完成之前,该设置将不会有任何效果。

使用函数更改设置和陷阱可以克服这一限制,至少在Homebrew Bash 5.2.15(1)中是这样

如果我从这个开始:

errexit_ignore()   {
set +e
trap -      ERR
}
errexit_fail() {
set -e
trap failed ERR
}
errexit_fail

我以后可以做一些非常有用的事情,比如:

for customization in ${customizations[@]}; do
log_mark 2 "Checking ${customization} ..."
errexit_ignore
diff -qs "${expected}/${customization}" "${tmpdir}/${customization}"
diff_exitcode="${?}"
errexit_fail
if [ "${diff_exitcode}" != "0" ]; then  ##  If it's not the expected file, the customization may have already been applied
errexit_ignore
diff -qs "${tmpdir}/${customization}" "${customized}/${customization}"
diff_exitcode="${?}"
errexit_fail
if [ "${diff_exitcode}" != "0" ]; then  ##  If it's neither the expected file nor the customized file, either default has changed or the customization has changed
errexit_ignore
git ls-files --error-unmatch "${customized}/${customization}"
track_exitcode="${?}"  ##  Detect untracked file
git diff --exit-code "${customized}/${customization}"
diff_exitcode="${?}"  ##  Detect modified tracked file
errexit_fail
if [ "${track_exitcode}" != "0" -o "${diff_exitcode}" != "0" ]; then  ##  If the customization has uncomitted changes, assume the default hasn't changed
log_mark 1 "Customized ${customization} will be updated"
else  ##  If the default has changed, manual review is needed (which may result in an updated customization)
diff -u "${expected}/${customization}" "${tmpdir}/${customization}" || :
diff -u "${tmpdir}/${customization}" "${customized}/${customization}" || :
abort "Default version of ${customization} has changed, expected version must be updated and customization must be checked for compatibility"
fi
else
log_mark 1 "Customized ${customization} already in place"
fi
else
log_mark 1 "Default ${customization} has not changed"
fi
done

注:

  • trap function ERR之后,除非您也使用trap - ERR,否则set +e不起作用
  • errexit_ignoreerrexit_fail都不能在一行上定义(我不确定为什么不能)

最新更新