我有一个由分阶段操作组成的脚本,这些操作主要以启动/停止/状态方式工作。
我遇到的问题是,启动时,每个操作都可能失败,我需要使用停止操作进行安全回滚。
我怎样才能在 bash 中做到这一点?
我正在考虑类似以下的事情(这是行不通的):
operation1_start() {}
operation2_start() {}
operation1_stop() {}
operation2_stop() {}
operation1_start && rollback=operation1_stop;$rollback
test_validity || $($rollback)
operation2_start && rollback=operation2_stop;$rollback
test_validity || $($rollback)
答案1
您会发现这两种 shell 编程技术很有用:
- 如果你跑
set -e
,那么如果命令返回非零状态(除非有明显含义的情况,例如if
或while
条件),则 shell 立即退出。 - 如果你跑
trap 'somecode' EXIT
,那么如果脚本退出(无论是显式退出,还是由于 隐式退出set -e
),somecode
首先执行。进入 时somecode
,$?
包含最后一个命令的状态。
因此你可以写类似的东西
(set -e; trap 'abort operation a' EXIT; perform operation a; )
在 bash 中,您可以在 上 设置陷阱ERR
而不是EXIT
;仅当 shell 由于set -e
.退出时才会执行此类陷阱。此外,ERR
陷阱对于函数来说是局部的。
set -e
operation_a () {
trap 'abort code' ERR
perform operation a
}
如果可以的话,首先准备草稿然后执行会更容易原子操作来提交您的交易。例如,如果您要写入文件,请写入目标目录中的临时文件,然后调用mv
将新文件移动到位。这比需要以回滚代码形式进行清理的任何内容都要强大得多,因为如果您的脚本因kill -9
电源故障而终止,则不会执行回滚。
答案2
我实际上设法使用数组来实现这一点。似乎工作正常(我什至添加了一个额外的参数告诉函数,这是一个回滚)。
abc()
{
echo "abc----$1";
}
cda()
{
echo "cda----$1";
}
safe_rollback=( )
add_rollback()
{
safe_rollback[${#safe_rollback[*]}]=$1;
}
run_rollback()
{
while [ ${#safe_rollback[@]} -ge 1 ]; do
${safe_rollback[${#safe_rollback[@]}-1]} rollback;
unset safe_rollback[${#safe_rollback[@]}-1];
done
}
add_rollback cda
add_rollback abc
run_rollback
答案3
您的操作实际上听起来像是构建过程的克隆,其中您可以根据各种情况执行启动/状态/继续/回滚/停止类型的操作。如果您将 Makefile 与 shell 脚本结合使用,您可能可以更优雅地实现该效果,因为可以更优雅地定义确定状态的相互依赖关系。