#!/usr/bin/env bash set -e # FIXME: clean up repetition ! read -rd '' FILTER_AWK <<'EOF' function flr(n, s) { return int(n / s) * s } BEGIN { if (match(retain, /([*0-9]+)h/, md)) hr = md[1] if (match(retain, /([*0-9]+)d/, md)) dr = md[1] if (match(retain, /([*0-9]+)w/, md)) wr = md[1] if (match(retain, /([*0-9]+)m/, md)) mr = md[1] } { t = mktime(gensub(/(.*\/|^)(.+)-(..)-(..)-(..)(..)(..)/, "\\2 \\3 \\4 \\5 \\6 \\7", 1), 1) h = flr(t, 60*60) d = flr(t, 24*60*60) m = mktime(strftime("%Y %m", t, 1) " 01 00 00 00", 1) w = m + flr(t - m, 7*24*60*60) } h in hkeep || hr == "*" || hc++ < hr { hkeep[h] = $1 } d in dkeep || dr == "*" || dc++ < dr { dkeep[d] = $1 } w in wkeep || wr == "*" || wc++ < wr { wkeep[w] = $1 } m in mkeep || mr == "*" || mc++ < mr { mkeep[m] = $1 } END { for (i in hkeep) keep[hkeep[i]]++ for (i in dkeep) keep[dkeep[i]]++ for (i in wkeep) keep[wkeep[i]]++ for (i in mkeep) keep[mkeep[i]]++ asorti(keep) for (i in keep) print keep[i] } EOF run() { echo "* ${@}" "${@}" } backup() { dest_dir="${1}" src_path="${2}" retain="${3:-"*h*d*w*m"}" [[ "$((dcount++))" -ne 0 ]] && echo echo "[${dcount}] [${retain}] ${src_path} => ${dest_dir}" mapfile -t snaps < <(shopt -s nullglob; printf '%s\n' "${dest_dir}/"* | sort -r | head -c -1) dest_path="${dest_dir}/$(date -u '+%Y-%m-%d-%H%M%S')" run rsync -ac --mkpath ${snaps[0]:+"--link-dest=${snaps[0]}"} \ "${src_path}" "${dest_path}" snaps=("${dest_path}" "${snaps[@]}") mapfile -t keep < <( awk -v "retain=${retain}" "${FILTER_AWK}" < <( printf '%s\n' "${snaps[@]}")) if [[ -z "${keep}" ]]; then echo "WARNING: dirs to keep shouldn't be empty; skipping" >&2 return fi for ((i = ${#snaps[@]} - 1, ki = 0; i >= 0; --i)); do if [[ "${snaps[i]}" == "${keep[ki]}" ]]; then ((++ki)) else run rm -r "${snaps[i]}" fi done printf 'kept %s\n' "${keep[@]}" } while IFS='=' read -r f1 f2; do [[ -z "${f1}" ]] && continue if [[ "${f1}" =~ ^\[(.*)]$ ]]; then base_path="${BASH_REMATCH[1]}" retain= continue fi if [[ "${f1}" == '$'* ]]; then if [[ "${f1}" == '$retain' ]]; then retain="${f2}" fi continue fi backup "${base_path}/${f1}" "${f2}" "${retain}" done < "${1}"