340 lines
7.2 KiB
Bash
340 lines
7.2 KiB
Bash
|
#!/bin/bash
|
|||
|
|
|||
|
SCRIPT_DESC="WebDav 备份脚本"
|
|||
|
SCRIPT_VERSION=1.0
|
|||
|
SCRIPT_AUTHOR=KAAAsS
|
|||
|
|
|||
|
show_help() {
|
|||
|
__opt() { printf " %-30s%s\n" "$1" "$2"; }
|
|||
|
echo "用法:"
|
|||
|
echo " $0 待备份文件路径"
|
|||
|
echo " $0 [选项] [--] 待备份文件路径"
|
|||
|
echo
|
|||
|
echo $SCRIPT_DESC
|
|||
|
echo
|
|||
|
echo "选项:"
|
|||
|
__opt "-x, --prefix <name>" "备份文件前缀"
|
|||
|
__opt "-s, --server <url>" "WebDav 服务器地址"
|
|||
|
__opt "-u, --user <user>" "WebDav 用户名"
|
|||
|
__opt "-p, --pass <password>" "WebDav 密码"
|
|||
|
__opt "-k, --keeps n" "保留最近几个备份文件。默认为0,不删除备份文件"
|
|||
|
__opt "-I, --ignore-cert" "忽略 WebDav 证书"
|
|||
|
echo
|
|||
|
__opt "-c, --config" "配置文件"
|
|||
|
__opt "-v, --verbose" "打印调试信息"
|
|||
|
__opt "-h, --help" "显示此帮助"
|
|||
|
__opt "-V, --version" "显示版本"
|
|||
|
echo
|
|||
|
}
|
|||
|
|
|||
|
#----------#
|
|||
|
# 可配置变量 #
|
|||
|
#----------#
|
|||
|
|
|||
|
verbose=false
|
|||
|
tmpfs="/tmp"
|
|||
|
prefix="backup"
|
|||
|
server="http://your-site/dav"
|
|||
|
user="guest"
|
|||
|
pass=""
|
|||
|
backup_path=-
|
|||
|
keeps=0
|
|||
|
retry=3
|
|||
|
ignore_cert=false
|
|||
|
curl_extra_args=
|
|||
|
|
|||
|
#--------
|
|||
|
# 脚本逻辑
|
|||
|
#--------
|
|||
|
|
|||
|
filename=-
|
|||
|
backup_dir=-
|
|||
|
|
|||
|
exit_handler() {
|
|||
|
local error_code="$?"
|
|||
|
# test $error_code == 0 && return;
|
|||
|
__dbg "清理临时文件..."
|
|||
|
if [[ -f $filename ]]; then
|
|||
|
__dbg " $filename"
|
|||
|
rm -rf $filename
|
|||
|
fi
|
|||
|
if [[ -d $backup_dir ]]; then
|
|||
|
__dbg " $backup_dir"
|
|||
|
rm -rf $backup_dir
|
|||
|
fi
|
|||
|
exit "$error_code"
|
|||
|
}
|
|||
|
|
|||
|
make_backup() {
|
|||
|
if [ ! -d $backup_path ]; then
|
|||
|
__err "待备份路径不存在!"
|
|||
|
exit 2
|
|||
|
fi
|
|||
|
|
|||
|
filename="$tmpfs/$prefix"_"$(date +%y%m%d_%H%M%S).tar.gz"
|
|||
|
backup_dir=$tmpfs/${prefix}_$(date +%H%M%S)
|
|||
|
|
|||
|
if [ -d $backup_dir ]; then
|
|||
|
rm -rf $backup_dir
|
|||
|
fi
|
|||
|
|
|||
|
__dbg "备份文件路径 $backup_path 至 $backup_dir"
|
|||
|
|
|||
|
echo -n "打包备份路径... "
|
|||
|
mkdir $backup_dir
|
|||
|
__try cp -r $backup_path $backup_dir
|
|||
|
__try tar -czvf $filename -C $backup_dir .
|
|||
|
|
|||
|
if __catch e; then
|
|||
|
__color $RED '错误\n'
|
|||
|
__err "打包失败: $e"
|
|||
|
exit 2
|
|||
|
fi
|
|||
|
|
|||
|
__color $GREEN '成功\n'
|
|||
|
__dbg "成功打包至: $filename"
|
|||
|
}
|
|||
|
|
|||
|
upload() {
|
|||
|
for retry_count in $( seq 0 $retry ); do
|
|||
|
|
|||
|
if [[ $retry_count -eq 0 ]]; then
|
|||
|
echo -n "上传备份文件... "
|
|||
|
else
|
|||
|
echo -n "上传备份文件: 第 $retry_count 次重试... "
|
|||
|
fi
|
|||
|
|
|||
|
__try curl -sS --user "$user:$pass" -T $1 "$server" -f $curl_extra_args
|
|||
|
|
|||
|
if __catch e; then
|
|||
|
__color $RED '错误\n'
|
|||
|
__err "上传失败: $e"
|
|||
|
continue
|
|||
|
fi
|
|||
|
|
|||
|
__color $GREEN '成功\n'
|
|||
|
return 0
|
|||
|
done
|
|||
|
|
|||
|
__err "超过失败重试次数"
|
|||
|
exit 3
|
|||
|
}
|
|||
|
|
|||
|
get_file_list() {
|
|||
|
flist_html=$(curl -sS --user "$user:$pass" -X PROPFIND "$server" -f --header 'Depth: 1' $curl_extra_args)
|
|||
|
echo $flist_html | grep -Po '<D:href>.+?\/\K(.+?)(?=<\/D:href>)' -o | grep $prefix | sort
|
|||
|
}
|
|||
|
|
|||
|
delete_file() {
|
|||
|
local file=$1
|
|||
|
|
|||
|
for retry_count in $( seq 0 $retry ); do
|
|||
|
|
|||
|
if [[ $retry_count -eq 0 ]]; then
|
|||
|
echo -n "删除备份 $file... "
|
|||
|
else
|
|||
|
echo -n "删除备份 $file: 第 $retry_count 次重试... "
|
|||
|
fi
|
|||
|
|
|||
|
__try curl -sS --user "$user:$pass" -X DELETE "$server$file" -f $curl_extra_args
|
|||
|
|
|||
|
if __catch e; then
|
|||
|
__color $RED '错误\n'
|
|||
|
__err "删除失败: $e"
|
|||
|
continue
|
|||
|
fi
|
|||
|
|
|||
|
__color $GREEN '成功\n'
|
|||
|
return 0
|
|||
|
done
|
|||
|
|
|||
|
__err "超过失败重试次数"
|
|||
|
exit 3
|
|||
|
}
|
|||
|
|
|||
|
remove_old_backup() {
|
|||
|
echo -n "检查已有备份... "
|
|||
|
__try get_file_list
|
|||
|
|
|||
|
if __catch e; then
|
|||
|
__err "无法取得服务器文件信息: $e"
|
|||
|
__err "删除旧备份失败!"
|
|||
|
__color $RED '错误\n'
|
|||
|
exit 4
|
|||
|
fi
|
|||
|
|
|||
|
__get_output file_list
|
|||
|
|
|||
|
local bk_count=$(echo $file_list | wc -w)
|
|||
|
|
|||
|
if [[ $bk_count -gt $keeps ]];then
|
|||
|
local del_count=`expr $bk_count - $keeps`
|
|||
|
|
|||
|
__color $BLUE "$del_count 个过时备份\n"
|
|||
|
__dbg "现存文件列表:$file_list"
|
|||
|
|
|||
|
# 删除文件
|
|||
|
local del_lst=$(echo $file_list | cut -d " " -f 1-$del_count)
|
|||
|
for file in $del_lst; do
|
|||
|
delete_file $file
|
|||
|
done
|
|||
|
else
|
|||
|
__color $GREEN '无需删除\n'
|
|||
|
__dbg "现存文件列表:$file_list"
|
|||
|
fi
|
|||
|
}
|
|||
|
|
|||
|
main() {
|
|||
|
trap exit_handler EXIT
|
|||
|
make_backup
|
|||
|
upload $filename
|
|||
|
if [[ $keeps -gt 0 ]]; then
|
|||
|
remove_old_backup
|
|||
|
fi
|
|||
|
}
|
|||
|
|
|||
|
#-------
|
|||
|
# Utils
|
|||
|
#-------
|
|||
|
|
|||
|
__init_util() {
|
|||
|
# DO NOT CALL ME OTHER THEN STARTUP
|
|||
|
_try_return=0
|
|||
|
_try_err=-
|
|||
|
_try_out=-
|
|||
|
}
|
|||
|
|
|||
|
GREEN='0;32' RED='0;31' BLUE='0;34'
|
|||
|
|
|||
|
__color() {
|
|||
|
c=$1
|
|||
|
shift 1
|
|||
|
printf "\033[${c}m$@\033[0m"
|
|||
|
}
|
|||
|
|
|||
|
__dbg() {
|
|||
|
if [ $verbose = true ]; then
|
|||
|
__color $BLUE "$@"
|
|||
|
echo
|
|||
|
fi
|
|||
|
}
|
|||
|
|
|||
|
__err() {
|
|||
|
__color $RED "$0: $@" >&2
|
|||
|
echo
|
|||
|
}
|
|||
|
|
|||
|
__try() {
|
|||
|
[[ $_try_return -eq 0 ]] && eval $({ ! _1=$({ _0=$($@); } 2>&1; echo -n "_try_out='$_0' _try_return=$? " >&2); echo -n "_try_err='$_1'"; } 2>&1)
|
|||
|
}
|
|||
|
|
|||
|
__catch() {
|
|||
|
_old_try=$_try_return
|
|||
|
_try_return=0
|
|||
|
export $1="$_try_err"
|
|||
|
[[ $_old_try -ne 0 ]]
|
|||
|
}
|
|||
|
|
|||
|
__get_output() {
|
|||
|
export $1="$_try_out"
|
|||
|
}
|
|||
|
|
|||
|
#--------#
|
|||
|
# 参数解析 #
|
|||
|
#--------#
|
|||
|
|
|||
|
set -o errexit -o pipefail -o noclobber -o nounset
|
|||
|
|
|||
|
! getopt --test > /dev/null
|
|||
|
if [[ ${PIPESTATUS[0]} -ne 4 ]]; then
|
|||
|
__err '环境缺少 getopt 以解析参数'
|
|||
|
exit 1
|
|||
|
fi
|
|||
|
|
|||
|
OPTIONS=c:vhVx:s:u:p:k:I
|
|||
|
LONGOPTS=config,verbose,help,version,prefix:,server:,user:,pass:,keeps:,ignore-cert
|
|||
|
|
|||
|
! PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@")
|
|||
|
if [[ ${PIPESTATUS[0]} -ne 0 ]]; then
|
|||
|
exit 1
|
|||
|
fi
|
|||
|
eval set -- "$PARSED"
|
|||
|
|
|||
|
while true; do
|
|||
|
case "$1" in
|
|||
|
-c|--config)
|
|||
|
# 解析配置文件
|
|||
|
if [ ! -f "$2" ]; then
|
|||
|
__err "配置文件不存在!"
|
|||
|
exit 1
|
|||
|
fi
|
|||
|
source $2
|
|||
|
shift 2
|
|||
|
;;
|
|||
|
-v|--verbose)
|
|||
|
verbose=true
|
|||
|
shift
|
|||
|
;;
|
|||
|
-h|--help)
|
|||
|
show_help
|
|||
|
exit 0
|
|||
|
;;
|
|||
|
-V|--version)
|
|||
|
echo $SCRIPT_VERSION
|
|||
|
exit 0
|
|||
|
;;
|
|||
|
-x|--prefix)
|
|||
|
prefix="$2"
|
|||
|
shift 2
|
|||
|
;;
|
|||
|
-s|--server)
|
|||
|
server="$2"
|
|||
|
shift 2
|
|||
|
;;
|
|||
|
-u|--user)
|
|||
|
user="$2"
|
|||
|
shift 2
|
|||
|
;;
|
|||
|
-p|--pass)
|
|||
|
pass="$2"
|
|||
|
shift 2
|
|||
|
;;
|
|||
|
-k|--keeps)
|
|||
|
keeps="$2"
|
|||
|
shift 2
|
|||
|
;;
|
|||
|
-I|--ignore-cert)
|
|||
|
ignore_cert=true
|
|||
|
shift
|
|||
|
;;
|
|||
|
--)
|
|||
|
# 结束参数解析
|
|||
|
shift
|
|||
|
break
|
|||
|
;;
|
|||
|
*)
|
|||
|
__err "参数解析失败"
|
|||
|
exit 1
|
|||
|
;;
|
|||
|
esac
|
|||
|
done
|
|||
|
|
|||
|
if [[ $ignore_cert = "true" ]]; then
|
|||
|
curl_extra_args="$curl_extra_args -k"
|
|||
|
fi
|
|||
|
|
|||
|
if [[ $# -gt 1 ]]; then
|
|||
|
__err "仅支持备份一个路径"
|
|||
|
exit 1
|
|||
|
elif [[ $# -eq 1 ]]; then
|
|||
|
backup_path=$1
|
|||
|
fi
|
|||
|
|
|||
|
if [[ $backup_path = - ]]; then
|
|||
|
__err "必须指定备份路径"
|
|||
|
exit 1
|
|||
|
fi
|
|||
|
|
|||
|
__init_util
|
|||
|
main
|