看看这个小脚本:
#!/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
[…]如果失败的命令是紧接在
while
或until
关键字之后的命令列表的一部分,是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_ignore
和errexit_fail
都不能在一行上定义(我不确定为什么不能)