如何写出幂等性的bash脚本

​ 这种情况一定发生过很多次,您编写了一个bash脚本,但是由于一个错误,脚本执行到一半退出了。你在你的系统中修复了这个错误然后再次运行了这个脚本。但是你脚本中一半的步骤很快就执行失败了,因为他们已经被安装在了你的系统中。为了构建可迅速恢复的系统你必须写出具有幂等性的软件。

什么是幂等性?

幂等性脚本能被多次执行并且每次被执行,它在系统中都会达到同样的效果。这就意味着,第二次执行也会以同样的结果退出并且将不会有任何副作用,引自据典:

幂等:代表当集合中的一个元素被其自身多次执行时其值不会被改变

好的软件总是以幂等的方法编写,特别是在分布式系统中,你可能会因为多次重复的请求执行多次最终回调函数,其操作最终往往是保持一致的(比如在队列中At-Least-Once 投递保证)。

Bash 参数

接下来将会介绍一系列bash 技巧以及参数用来让你的脚本是幂等的。你很可能在用这些参数的时候都没有意识到这些参数的副作用

新建一个空文件

touch是默认幂等的。它意味着你调用多次都没有问题。第二次调用对文本内容没有任何影响。需要注意的是它将会更新文件的修改时间,所以如果你依赖它,要小心。

touch example.txt

新建一个文件夹

不要直接用 mkdir , 记得加 -p 参数。如果文件夹已经存在,-p 可以让mkdir不报错

mkdir -p mydir

添加一个软连接

我们可以用下面的命令添加一个软连接:

ln -s source target

但是如果你对同一个target再次执行的话这个命令就会失败。可以通过-f 参数让它是幂等的:

ln -sf source target

当连接一个文件夹时,你也需要添加```-n```参数。否则一旦再次调用它,该命令将会在该文件夹下添加一个软连接。

```bash
mkdir a 
ln -sf a b
ln -sf a b
ls a 
a 

所以为了安全,请经常用ln -sfn source target

删除文件夹

删除一个文件夹:

rm example.txt

-f参数将会忽视文件是否存在:

rm -f example.txt

修改文件

有时候你需要向一个已经存在的文件中新增一行(如:/etc/fstab)。这意味着当你运行你的脚本的时候你需要确保它不会被多次添加。假设你在你的脚本中有这样一行命令:

echo "/dev/sda1 /mnt/dev ext4 defaults 0 0" | sudo tee -a /etc/fstab

如果这行代码再次被运行, 脚本将因为在你的/etc/fstab中有重复的条目而终止。让其是幂等的一种方法是通过grep 命令检查某个占位符:

if ! grep -qF "/mnt/dev" /etc/fstab; then
  echo "/dev/sda1 /mnt/dev ext4 defaults 0 0" | sudo tee -a /etc/fstab
fi

这里的-q代表slient mode ,-F开启固定字符串(fixed string mode),grep将会缄默失败如果/mnt/dev 不存在所以 echo 将不会被调用

检查文件夹、文件、变量是否存在

大多数时候,你都是向一个文件夹中写入,读取某个文件或者使用变量进行一些简单的字符串操作。比如你可能基于某种输入新建一个文件:

echo "complex set of rules" > /etc/conf/foo.txt

文本计算可能是一个成本高昂的操作,确保你不想每次调用脚本的时候都写入它。你可以通过-f参数检查文件是否存在

if [ ! -f "/etc/conf/foo.txt" ]; then
 echo "complex set of rules" > /etc/conf/foo.txt
fi

这里只选用了-f作为例子,还有很多其他的参数你可以使用,比如:

  • -d :文件夹
  • -z: 零长度字符串
  • -p: 管道(pipe)
  • -x: 文件并有执行权限

假如你想安装一个二进制,如果它不存在你的主机中的话。你可以利用-x像下面这样:

# install 1password CLI
if ! [ -x "$(command -v op)" ]; then
  export OP_VERSION="v0.5.6-003"
  curl -sS -o 1password.zip https://cache.agilebits.com/dist/1P/op/pkg/${OP_VERSION}/op_linux_amd64_${OP_VERSION}.zip
  unzip 1password.zip op -d /usr/local/bin
  rm -f 1password.zip
fi

以上命令将会把op 安装到/usr/local/bin。如果你再次运行你的脚本,它将不会再次安装。另一个好处是,你可以很容易地升级它到一个新的版本通过从系统中移除,升级OP_VERSION然后再次运行你的脚本

可以通过执行man test来获得完整的参数和命令

格式化一个设备

为了格式化一个磁盘,可以用ext4命令,你可以像下面这样来调用这个命令

mkfs.ext4 "$VOLUME_NAME"

当然,如果你再次调用,它会失败。你可以通过在前面加上blkid来让这个调用幂等:

blkid "$VOLUME_NAME" || mkfs.ext4 "$VOLUME_NAME"

这个命令


  TOC