webdav_backup_sh/webdav_backup.sh
2022-03-11 14:03:19 +08:00

348 lines
7.4 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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 "-P, --server-path <path>" "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"
server_path=
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$server_path" -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$server_path" -f --header 'Depth: 1' $curl_extra_args)
echo $flist_html | grep -Po '<D:href>.+?\/\K(.+?)(?=<\/D:href>)' -o | grep $server_path$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() {
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
}
__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:P:u:p:k:I
LONGOPTS=config,verbose,help,version,prefix:,server:,server-path:,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
;;
-P|--server-path)
server_path="$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