2022-02-13 19:48:02 +08:00
|
|
|
|
#!/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>" "备份文件前缀"
|
2022-02-13 20:01:16 +08:00
|
|
|
|
__opt "-s, --server <url>" "WebDav 服务器地址,以 \"/\" 结束"
|
|
|
|
|
__opt "-P, --server-path <path>" "WebDav 备份路径,以 \"/\" 结束"
|
2022-02-13 19:48:02 +08:00
|
|
|
|
__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"
|
2022-02-13 20:01:16 +08:00
|
|
|
|
server_path=
|
2022-02-13 19:48:02 +08:00
|
|
|
|
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
|
|
|
|
|
|
2022-02-13 20:01:16 +08:00
|
|
|
|
__try curl -sS --user "$user:$pass" -T $1 "$server$server_path" -f $curl_extra_args
|
2022-02-13 19:48:02 +08:00
|
|
|
|
|
|
|
|
|
if __catch e; then
|
|
|
|
|
__color $RED '错误\n'
|
|
|
|
|
__err "上传失败: $e"
|
|
|
|
|
continue
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
__color $GREEN '成功\n'
|
|
|
|
|
return 0
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
__err "超过失败重试次数"
|
|
|
|
|
exit 3
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get_file_list() {
|
2022-02-13 20:01:16 +08:00
|
|
|
|
flist_html=$(curl -sS --user "$user:$pass" -X PROPFIND "$server$server_path" -f --header 'Depth: 1' $curl_extra_args)
|
2022-03-11 12:58:10 +08:00
|
|
|
|
echo $flist_html | grep -Po '<D:href>.+?\/\K(.+?)(?=<\/D:href>)' -o | grep $server_path$prefix | sort
|
2022-02-13 19:48:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
2022-03-11 12:58:10 +08:00
|
|
|
|
__try curl -sS --user "$user:$pass" -X DELETE "$server$file" -f $curl_extra_args
|
2022-02-13 19:48:02 +08:00
|
|
|
|
|
|
|
|
|
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() {
|
2022-02-13 20:15:52 +08:00
|
|
|
|
if [[ $_try_return -eq 0 ]]; then
|
|
|
|
|
eval $({ ! _1=$({ _0=$($@); } 2>&1; echo -n "_try_out='$_0' _try_return=$? " >&2); echo -n "_try_err='$_1'"; } 2>&1)
|
|
|
|
|
fi
|
2022-02-13 19:48:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
__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
|
|
|
|
|
|
2022-02-13 20:01:16 +08:00
|
|
|
|
OPTIONS=c:vhVx:s:P:u:p:k:I
|
|
|
|
|
LONGOPTS=config,verbose,help,version,prefix:,server:,server-path:,user:,pass:,keeps:,ignore-cert
|
2022-02-13 19:48:02 +08:00
|
|
|
|
|
|
|
|
|
! 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
|
|
|
|
|
;;
|
2022-02-13 20:01:16 +08:00
|
|
|
|
-P|--server-path)
|
|
|
|
|
server_path="$2"
|
|
|
|
|
shift 2
|
|
|
|
|
;;
|
2022-02-13 19:48:02 +08:00
|
|
|
|
-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
|