起因

今天再一次部署更新G应用的时候,按常规我将原来的运行文件做了备份,以作为万一紧急回滚之需:

mv G.jar G.jar.bak.7

我习惯性的ll,发现之前的备份文件已经有6个了,没有必要都保留,于是删除了几个备份文件

rm G.jar.bak.1
rm G.jar.bak.2
rm G.jar.bak.3
rm G.jar.bak.4

这时,我懒癌忽然爆发,不想每次都敲那么多字来维护备份,希望写一个脚本,自动备份文件,但只保留最新的n个备份文件。

设计

抽象需求:对指定目录${path}的指定名字${file}的文件进行备份,添加.bak.num后缀,如果备份文件大于指定的数目${keepNum},则删除最老的几个备份文件。其中,num是0到${keepNum}-1的数字,新生成的备份文件是之前最新备份文件的num+1。

实现

  • 找到所有指定备份文件
find ${path} -name "${file}"
  • 按时间排序
find ${path} -name "${file}" -printf "%T+ %p\n" | sort -n
  • 按列切割出文件名
find ${path} -name "${file}" -printf "%T+ %p\n" | sort -n | awk '{print $2}'
  • 包装为函数,以便复用,
## params: path, file
function getFiles(){
    return find $1 -name "$2" -printf "%T+ %p\n" | sort -n | awk '{print $2}'
}

然而shell函数只能返回int! 这悲剧了。退而求其次:

  • 保存文件名结果为变量,待用
files="`find ${path} -name "${file}" -printf "%T+ %p\n" | sort -n | awk '{print $2}'`"
for f in ${files}
do
  echo $f
done

这是可以的。

  • 小坑1:算术运算 shell不支持直接的算术运算,而是使用expr
#4
echo `expr 2 + 2`

当然也可以用扩展参数的办法:

i=$[1 + 1]
#2
echo $i
  • 小坑2:引用赋值,等号前后都不能有空格
val='fdf'
# correct.
val2=val
# wrong!
val3 = val

  • tips:数组长度
arr=(1 2 3)
#3
echo ${#arr[*]}
  • tips:用指定的分隔符切割字符串为数组
f="aaa.bbb.18"
#arr=(aaa bbb 18)
arr=(${f//./ })

原理是:使用参数扩展,将分隔符”.”替换为空格,见split a string in shell

完整的代码

#backup.sh
file=$1

path="."
backupFiles="${file}.bak.*"

if [ -n "$2"]; then
  keepNum=5
else
  keepNum=$2
fi

find ${path} -name "${backupFiles}" -printf "%T+ %p\n" | sort -n | awk '{print $2}'

files="`find ${path} -name "${backupFiles}" -printf "%T+ %p\n" | sort -n | awk '{print $2}'`"
fileNum=`find ${path} -name "${backupFiles}" -printf "%T+ %p\n" | sort -n | awk '{print $2}' |wc -l`

i=0

for f in ${files}
do
  if test $i -lt `expr ${fileNum} - ${keepNum}`
  then
     rm -rf $f
     echo "rm old backup file or dir $f"
  fi
  i=`expr $i + 1`
  latestFile=$f

done

#seperated by .
arr=(${f//./ })
#get the last part
i=`expr ${#arr[*]} - 1`
num=${arr[i]}

echo "cp -rf ${file} ${file}.bak.`expr ${num} + 1`"
cp -rf ${file} ${file}.bak.`expr ${num} + 1`

使用

有了backup.sh脚本,就可以在部署脚本里调用它,不用再手工重复敲命令了。

总结

shell编程的语法还是有点奇怪的,多试多练,唯手熟尔。