From 314ad8d94c5ff33fd2eadb1027748550be0e7e4f Mon Sep 17 00:00:00 2001 From: Erich Eckner Date: Sun, 10 Feb 2019 22:36:04 +0100 Subject: camelCase -> no-camel-case, backup-progress new --- .gitignore | 9 +- Makefile | 12 +- backup-progress.in | 30 +++++ backup-statistics.in | 346 ++++++++++++++++++++++++++++++++++++++++++++++++++ backup.conf | 4 +- backupStatistics.in | 346 -------------------------------------------------- check-diff | 28 ++++ checkDiff | 28 ---- fast-repair.in | 88 +++++++++++++ fastRepair.in | 88 ------------- last-backups.in | 110 ++++++++++++++++ lastBackups.in | 110 ---------------- lastBackups.in.orig | 73 ----------- man.commons.in | 13 +- remove-old-backups.in | 79 ++++++++++++ removeOldBackups.in | 79 ------------ 16 files changed, 704 insertions(+), 739 deletions(-) create mode 100755 backup-progress.in create mode 100644 backup-statistics.in delete mode 100644 backupStatistics.in create mode 100755 check-diff delete mode 100755 checkDiff create mode 100644 fast-repair.in delete mode 100644 fastRepair.in create mode 100755 last-backups.in delete mode 100755 lastBackups.in delete mode 100755 lastBackups.in.orig create mode 100755 remove-old-backups.in delete mode 100755 removeOldBackups.in diff --git a/.gitignore b/.gitignore index 08432d6..b917e42 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,11 @@ backup backup@.service backup@.timer -backupStatistics -fastRepair -lastBackups -removeOldBackups +backup-progress +backup-statistics +fast-repair +last-backups +remove-old-backups man.commons *.common *.1 diff --git a/Makefile b/Makefile index 639a375..49ee698 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,13 @@ MANDIR = /usr/share/man VERSION = 1.3.4 -all: man.commons backup backup.1 backup@.service backup@.timer backupStatistics backupStatistics.1 lastBackups lastBackups.1 removeOldBackups removeOldBackups.1 +all: man.commons \ + backup backup.1 \ + backup@.service backup@.timer \ + backup-progress \ + backup-statistics backup-statistics.1 \ + last-backups last-backups.1 \ + remove-old-backups remove-old-backups.1 %: %.in sed " \ @@ -47,9 +53,9 @@ all: man.commons backup backup.1 backup@.service backup@.timer backupStatistics .PHONY: install dist clean install: all - install -D -m0755 -t $(DESTDIR)$(BINDIR) backup backupStatistics lastBackups removeOldBackups + install -D -m0755 -t $(DESTDIR)$(BINDIR) backup backup-progress backup-statistics last-backups remove-old-backups install -D -m0644 -t $(DESTDIR)$(LIBDIR)/systemd/system backup@.service backup@.timer - install -D -m0644 -t $(DESTDIR)$(MANDIR)/man1 backup.1 backupStatistics.1 lastBackups.1 removeOldBackups.1 + install -D -m0644 -t $(DESTDIR)$(MANDIR)/man1 backup.1 backup-statistics.1 last-backups.1 remove-old-backups.1 install -D -m0644 -t $(DESTDIR)$(ETCDIR) backup.conf clean: diff --git a/backup-progress.in b/backup-progress.in new file mode 100755 index 0000000..c5cb655 --- /dev/null +++ b/backup-progress.in @@ -0,0 +1,30 @@ +#!/bin/bash + +. #ETCDIR#/backup.conf + +export LC_ALL=C + +for backupID in "${!backups[@]}"; do + [ -s "/tmp/${backupID}.pid" ] || continue + kill -0 $(cat "/tmp/${backupID}.pid") || continue + dir="${backups["${backupID}"]%% *}" + last=$( + ls -1 "$dir" \ + | grep '[0-9]\{4\}_[0-9]\{2\}_[0-9]\{2\}' \ + | sort \ + | tail -n1 + ) + last_size=$( + du -s "${dir}/${last}" \ + | awk '{print $1}' + ) + current_size=$( + du -s "${dir}/aktuell" \ + | awk '{print $1}' + ) + percentage=$( + printf '100*%s/%s\n' "${current_size}" "${last_size}" \ + | bc -l + ) + printf '%6.2f %% %s\n' "${percentage}" "${backupID}" +done diff --git a/backup-statistics.in b/backup-statistics.in new file mode 100644 index 0000000..a44f41d --- /dev/null +++ b/backup-statistics.in @@ -0,0 +1,346 @@ +#!/bin/bash + +# backup-statistics version #VERSION# + +set -e + +[ -r "#ETCDIR#/backup.conf" ] && \ + . "#ETCDIR#/backup.conf" + +do_stage() +{ + case $1 in + 1) + if [ "$2" == '##DESCRIBE##' ] + then + echo 'generate lists $filename -> $inode' + return 0 + fi + dest="${backups["${backupID}"]%% *}" + dest="${dest%/}" + while [ ! -d "${dest}" ] && [ ${maxWait} -gt 0 ] + do + sleep 1 + maxWait=$[${maxWait}-1] + done + + rm -f "${cacheDir}/${backupID}.inodes" + touch "${cacheDir}/${backupID}.inodes" + chmod go-rwx "${cacheDir}/${backupID}.inodes" + for dat in $(ls "${dest}") + do + echo "${dat}:" + find "${dest}/${dat}" -type f -links -64001 -printf '%i %D-%m-%U-%G %p\n' >> \ + "${cacheDir}/${backupID}.inodes" + done + ;; + 2) + if [ "$2" == '##DESCRIBE##' ] + then + echo 'sort previous lists by $inode' + return 0 + fi + tmpDirA="$(mktemp -d)" + tmpDirB="$(mktemp -d "${cacheDir}/tmp.XXXXXX")" + touch "${cacheDir}/${backupID}.inodes.sorted" + chmod go-rwx "${cacheDir}/${backupID}.inodes.sorted" + sort -T "${tmpDirA}" -T "${tmpDirB}" -u "${cacheDir}/${backupID}.inodes" > \ + "${cacheDir}/${backupID}.inodes.sorted" + rmdir "${tmpDirA}" "${tmpDirB}" + ;; + 3) + if [ "$2" == '##DESCRIBE##' ] + then + echo 'generate lists $inode -> $count, $contentHash' + return 0 + fi + touch "${cacheDir}/${backupID}.content" + chmod go-rwx "${cacheDir}/${backupID}.content" + uniq -cm2 "${cacheDir}/${backupID}.inodes.sorted" | \ + parallel \ + sha512sum {=s/^ *\([[:digit:]]\+ \)\{2\}[0-9-]\+ //=} \| \ + sed '"s|^\([0-9a-f]\{128\}\) .*\$|\1'{=s/^ *\([[:digit:]]\+ [[:digit:]]\+\) \([0-9-]\+\) .*/-\\2 \\1/=}'|"' \ + \; > \ + "${cacheDir}/${backupID}.content" + ;; + 4) + if [ "$2" == '##DESCRIBE##' ] + then + echo 'sort previous lists by $contentHash' + return 0 + fi + tmpDirA="$(mktemp -d)" + tmpDirB="$(mktemp -d "${cacheDir}/tmp.XXXXXX")" + touch "${cacheDir}/${backupID}.content.sorted" + chmod go-rwx "${cacheDir}/${backupID}.content.sorted" + sort -T "${tmpDirA}" -T "${tmpDirB}" -k1,1 -k2nr,2 "${cacheDir}/${backupID}.content" > \ + "${cacheDir}/${backupID}.content.sorted" + rmdir "${tmpDirA}" "${tmpDirB}" + ;; + 5) + if [ "$2" == '##DESCRIBE##' ] + then + echo 'generate sorted lists of groups of inodes with the same hashes' + return 0 + fi + index=0 + tmpDirA="$(mktemp -d)" + tmpDirB="$(mktemp -d "${cacheDir}/tmp.XXXXXX")" + touch "${cacheDir}/${backupID}.duplicates" + chmod go-rwx "${cacheDir}/${backupID}.duplicates" + uniq -m1 --all-repeated=separate "${cacheDir}/${backupID}.content.sorted" | \ + sed 's|^\(\S\+ \)\{2\}||' | \ + while read s + do + if [ -z "${s}" ] + then + index=$[${index}+1] + else + echo "${s#* } B ${index}" + fi + done | \ + sort -T "${tmpDirA}" -T "${tmpDirB}" > \ + "${cacheDir}/${backupID}.duplicates" + rmdir "${tmpDirA}" "${tmpDirB}" + ;; + 6) + if [ "$2" == '##DESCRIBE##' ] + then + echo 'find files to inodes of previous lists' + return 0 + fi + tmpDirA="$(mktemp -d)" + tmpDirB="$(mktemp -d "${cacheDir}/tmp.XXXXXX")" + + unset block + unset lastBlock + unset firstInode + unset lastInode + + touch "${cacheDir}/${backupID}.duplicates.files" + chmod go-rwx "${cacheDir}/${backupID}.duplicates.files" + sed ' + s|^\(\S\+\) \S\+ |\1 F | + ' "${cacheDir}/${backupID}.inodes.sorted" | \ + sort -m -T "${tmpDirA}" -T "${tmpDirB}" -- \ + - "${cacheDir}/${backupID}.duplicates" | \ + while read -r inode type extra + do + if [ "${type}" == "B" ] + then + block="${extra}" + elif [ "${lastInode}" == "${inode}" ] && [ -n "${block}" ] + then + echo "${block} ${inode} ${extra}" + else + unset block + fi + lastInode="${inode}" + done | \ + sort -T "${tmpDirA}" -T "${tmpDirB}" -k1n,1 | \ + while read -r block inode extra + do + if [ "${lastBlock}" != "${block}" ] + then + firstInode="${inode}" + fi + if [ "${lastBlock}" != "${block}" ] || [ "${firstInode}" != "${inode}" ] + then + echo "${block} ${extra}" + fi + lastBlock="${block}" + done | \ + uniq -m1 --group=separate > \ + "${cacheDir}/${backupID}.duplicates.files" + rmdir "${tmpDirA}" "${tmpDirB}" + ;; + 7) + if [ "$2" == '##DESCRIBE##' ] + then + echo 'relink files with different inodes and same hashes' + return 0 + fi + if [ ! -r "${cacheDir}/next.action" ] + then + cat "${cacheDir}/${backupID}.duplicates.files" + elif [ "$(head -n1 "${cacheDir}/next.action")" == "${backupID}" ] + then + startBlock="$(tail -n1 "${cacheDir}/next.action")" + sed " + :vor; + /^${startBlock} /bnach; + d; + bvor; + :nach; + n; + bnach + " "${cacheDir}/${backupID}.duplicates.files" + fi | \ + while read -r oBlock original + do + echo "${backupID}" > "${cacheDir}/next.action2" + echo "${oBlock}" >> "${cacheDir}/next.action2" + mv "${cacheDir}/next.action2" "${cacheDir}/next.action" + while read -r kBlock kopie + do + [ -z "${kopie}" ] && break + if [ "${kBlock}" != "${oBlock}" ] + then + >&2 echo "'${kBlock}' != '${oBlock}'" + >&2 echo "'${backupID}':" + >&2 echo "'${original}'" + >&2 echo "'${kopie}'" + exit 1 + fi + + if ${paranoid} + then + diff "${original}" "${kopie}" + fi + if [ $(stat -c'%h' "${original}") -ge 65000 ] + then + echo "rm \"${original}\"" + echo "ln \"${kopie}\" \"${original}\"" + if ! ${dummy} + then + rm "${original}" + ln "${kopie}" "${original}" + fi + else + echo "rm \"${kopie}\"" + echo "ln \"${original}\" \"${kopie}\"" + if ! ${dummy} + then + rm "${kopie}" + ln "${original}" "${kopie}" + fi + fi + done + done + if [ -r "${cacheDir}/next.action" ] && \ + [ "$(head -n1 "${cacheDir}/next.action")" == "${backupID}" ] + then + rm -f "${cacheDir}/next.action" "${cacheDir}/next.action2" + fi + ;; + esac +} + +usage() +{ + >&2 echo \ +'Usage: backup-statistics [OPTION] +Search and tidy duplicate and not-hardlinked files in the backups. + +With no options, tidy up all backups. THIS CAN BE VERY TIME CONSUMING. + +Mandatory arguments to long options are mandatory for short options too. + -d, --dummy only generate lists, do not modify backupfiles + -m, --max=maxNum stop execution after step maxNum + -p, --paranoid test for file differences before relinking (test _should_ be obsolete) + -s, --skip=skipNum skip first skipNum steps +#HELPTEXT# # + +the executed steps are:' + + for ((stage=1; stage<=#NUMSTAGES#; stage++)) + do + >&2 echo '' + >&2 echo " ${stage}. $(do_stage ${stage} '##DESCRIBE##')" + done + >&2 echo '' + [ -z "$1" ] && exit 1 + exit $1 +} + +eval set -- "$( + getopt -o dm:ps: \ + --long dummy \ + --long help \ + --long max: \ + --long paranoid \ + --long skip: \ + --long version \ + -n "$(basename "$0")" -- "$@" || \ + echo usage +)" + +dummy=false +maxNum=#NUMSTAGES# +paranoid=false +skipNum=0 + +while true; do + case "$1" in + -d|--dummy) + dummy=true + ;; + --help) + usage 0 + ;; + -m|--max) + shift + maxNum=$1 + ;; + -p|--paranoid) + paranoid=true + ;; + -s|--skip) + shift + skipNum=$1 + ;; + --version) + >&2 echo '#VERSION#' + exit 0 + ;; + --) + shift + [ $# -gt 0 ] && echo 'ERROR: Unknown parameter: '"$#" && usage + break + ;; + *) + >&2 echo 'That should not happen, '"$1"' unknown though ...' + exit -1 + ;; + esac + shift +done + +if [ ! -d "${cacheDir}" ] || [ -z "${cacheDir}" ] +then + >&2 "ERROR: Cache directory must exist, '${cacheDir}' does not! Closing." + exit 1 +fi + +( + echo -n 'Signature: ' + echo -n '.IsCacheDirectory' | \ + md5sum - | \ + cut -d ' ' -f 1 + echo '# This file is a cache directory tag created by '"$(basename "$0")"'.' + echo '# For information about cache directory tags, see:' + echo '# http://www.brynosaurus.com/cachedir/' +) > "${cacheDir}/CACHEDIR.TAG" +( + echo '+ .rsync-filter' + echo '- *' +) > "${cacheDir}/.rsync-filter" + +if [ ! "${skipNum}" -ge 0 ] || \ + [ ! "${skipNum}" -le #NUMSTAGES# ] || \ + [ ! "${maxNum}" -ge 0 ] || \ + [ ! "${maxNum}" -le #NUMSTAGES# ] +then + usage +fi + +for ((stage=${skipNum}+1; stage<=${maxNum}; stage++)) +do + echo "entering stage ${stage} ($(do_stage ${stage} '##DESCRIBE##')) ..." + for backupID in "${!backups[@]}" + do + echo "${backupID}:" + do_stage ${stage} "${backupID}" + done + echo "... stage ${stage} completed." +done diff --git a/backup.conf b/backup.conf index 6043fb3..8d7fd4e 100644 --- a/backup.conf +++ b/backup.conf @@ -17,7 +17,7 @@ backups["proxiedBackup"]="/path/to/destination/ user@source:path proxy_user@ssh_ # which directories must be mounted before we can run successfully neededMounts=('/data') -# how long to wait for destination directories to appear in lastBackups [seconds] +# how long to wait for destination directories to appear in last-backups [seconds] maxWait=100 # how old may (seldom) backups be before we warn about outdated ones [seconds] @@ -35,5 +35,5 @@ seldomness=14 # subdirectories which should be appended to the parent directory in the report recognSubdirRegex="home\|boot\|root\|erich" -# directory for caching valuable information in backupStatistics +# directory for caching valuable information in backup-statistics cacheDir="/path/to/cache/directory" diff --git a/backupStatistics.in b/backupStatistics.in deleted file mode 100644 index eb66e2c..0000000 --- a/backupStatistics.in +++ /dev/null @@ -1,346 +0,0 @@ -#!/bin/bash - -# backupStatistics version #VERSION# - -set -e - -[ -r "#ETCDIR#/backup.conf" ] && \ - . "#ETCDIR#/backup.conf" - -do_stage() -{ - case $1 in - 1) - if [ "$2" == '##DESCRIBE##' ] - then - echo 'generate lists $filename -> $inode' - return 0 - fi - dest="${backups["${backupID}"]%% *}" - dest="${dest%/}" - while [ ! -d "${dest}" ] && [ ${maxWait} -gt 0 ] - do - sleep 1 - maxWait=$[${maxWait}-1] - done - - rm -f "${cacheDir}/${backupID}.inodes" - touch "${cacheDir}/${backupID}.inodes" - chmod go-rwx "${cacheDir}/${backupID}.inodes" - for dat in $(ls "${dest}") - do - echo "${dat}:" - find "${dest}/${dat}" -type f -links -64001 -printf '%i %D-%m-%U-%G %p\n' >> \ - "${cacheDir}/${backupID}.inodes" - done - ;; - 2) - if [ "$2" == '##DESCRIBE##' ] - then - echo 'sort previous lists by $inode' - return 0 - fi - tmpDirA="$(mktemp -d)" - tmpDirB="$(mktemp -d "${cacheDir}/tmp.XXXXXX")" - touch "${cacheDir}/${backupID}.inodes.sorted" - chmod go-rwx "${cacheDir}/${backupID}.inodes.sorted" - sort -T "${tmpDirA}" -T "${tmpDirB}" -u "${cacheDir}/${backupID}.inodes" > \ - "${cacheDir}/${backupID}.inodes.sorted" - rmdir "${tmpDirA}" "${tmpDirB}" - ;; - 3) - if [ "$2" == '##DESCRIBE##' ] - then - echo 'generate lists $inode -> $count, $contentHash' - return 0 - fi - touch "${cacheDir}/${backupID}.content" - chmod go-rwx "${cacheDir}/${backupID}.content" - uniq -cm2 "${cacheDir}/${backupID}.inodes.sorted" | \ - parallel \ - sha512sum {=s/^ *\([[:digit:]]\+ \)\{2\}[0-9-]\+ //=} \| \ - sed '"s|^\([0-9a-f]\{128\}\) .*\$|\1'{=s/^ *\([[:digit:]]\+ [[:digit:]]\+\) \([0-9-]\+\) .*/-\\2 \\1/=}'|"' \ - \; > \ - "${cacheDir}/${backupID}.content" - ;; - 4) - if [ "$2" == '##DESCRIBE##' ] - then - echo 'sort previous lists by $contentHash' - return 0 - fi - tmpDirA="$(mktemp -d)" - tmpDirB="$(mktemp -d "${cacheDir}/tmp.XXXXXX")" - touch "${cacheDir}/${backupID}.content.sorted" - chmod go-rwx "${cacheDir}/${backupID}.content.sorted" - sort -T "${tmpDirA}" -T "${tmpDirB}" -k1,1 -k2nr,2 "${cacheDir}/${backupID}.content" > \ - "${cacheDir}/${backupID}.content.sorted" - rmdir "${tmpDirA}" "${tmpDirB}" - ;; - 5) - if [ "$2" == '##DESCRIBE##' ] - then - echo 'generate sorted lists of groups of inodes with the same hashes' - return 0 - fi - index=0 - tmpDirA="$(mktemp -d)" - tmpDirB="$(mktemp -d "${cacheDir}/tmp.XXXXXX")" - touch "${cacheDir}/${backupID}.duplicates" - chmod go-rwx "${cacheDir}/${backupID}.duplicates" - uniq -m1 --all-repeated=separate "${cacheDir}/${backupID}.content.sorted" | \ - sed 's|^\(\S\+ \)\{2\}||' | \ - while read s - do - if [ -z "${s}" ] - then - index=$[${index}+1] - else - echo "${s#* } B ${index}" - fi - done | \ - sort -T "${tmpDirA}" -T "${tmpDirB}" > \ - "${cacheDir}/${backupID}.duplicates" - rmdir "${tmpDirA}" "${tmpDirB}" - ;; - 6) - if [ "$2" == '##DESCRIBE##' ] - then - echo 'find files to inodes of previous lists' - return 0 - fi - tmpDirA="$(mktemp -d)" - tmpDirB="$(mktemp -d "${cacheDir}/tmp.XXXXXX")" - - unset block - unset lastBlock - unset firstInode - unset lastInode - - touch "${cacheDir}/${backupID}.duplicates.files" - chmod go-rwx "${cacheDir}/${backupID}.duplicates.files" - sed ' - s|^\(\S\+\) \S\+ |\1 F | - ' "${cacheDir}/${backupID}.inodes.sorted" | \ - sort -m -T "${tmpDirA}" -T "${tmpDirB}" -- \ - - "${cacheDir}/${backupID}.duplicates" | \ - while read -r inode type extra - do - if [ "${type}" == "B" ] - then - block="${extra}" - elif [ "${lastInode}" == "${inode}" ] && [ -n "${block}" ] - then - echo "${block} ${inode} ${extra}" - else - unset block - fi - lastInode="${inode}" - done | \ - sort -T "${tmpDirA}" -T "${tmpDirB}" -k1n,1 | \ - while read -r block inode extra - do - if [ "${lastBlock}" != "${block}" ] - then - firstInode="${inode}" - fi - if [ "${lastBlock}" != "${block}" ] || [ "${firstInode}" != "${inode}" ] - then - echo "${block} ${extra}" - fi - lastBlock="${block}" - done | \ - uniq -m1 --group=separate > \ - "${cacheDir}/${backupID}.duplicates.files" - rmdir "${tmpDirA}" "${tmpDirB}" - ;; - 7) - if [ "$2" == '##DESCRIBE##' ] - then - echo 'relink files with different inodes and same hashes' - return 0 - fi - if [ ! -r "${cacheDir}/next.action" ] - then - cat "${cacheDir}/${backupID}.duplicates.files" - elif [ "$(head -n1 "${cacheDir}/next.action")" == "${backupID}" ] - then - startBlock="$(tail -n1 "${cacheDir}/next.action")" - sed " - :vor; - /^${startBlock} /bnach; - d; - bvor; - :nach; - n; - bnach - " "${cacheDir}/${backupID}.duplicates.files" - fi | \ - while read -r oBlock original - do - echo "${backupID}" > "${cacheDir}/next.action2" - echo "${oBlock}" >> "${cacheDir}/next.action2" - mv "${cacheDir}/next.action2" "${cacheDir}/next.action" - while read -r kBlock kopie - do - [ -z "${kopie}" ] && break - if [ "${kBlock}" != "${oBlock}" ] - then - >&2 echo "'${kBlock}' != '${oBlock}'" - >&2 echo "'${backupID}':" - >&2 echo "'${original}'" - >&2 echo "'${kopie}'" - exit 1 - fi - - if ${paranoid} - then - diff "${original}" "${kopie}" - fi - if [ $(stat -c'%h' "${original}") -ge 65000 ] - then - echo "rm \"${original}\"" - echo "ln \"${kopie}\" \"${original}\"" - if ! ${dummy} - then - rm "${original}" - ln "${kopie}" "${original}" - fi - else - echo "rm \"${kopie}\"" - echo "ln \"${original}\" \"${kopie}\"" - if ! ${dummy} - then - rm "${kopie}" - ln "${original}" "${kopie}" - fi - fi - done - done - if [ -r "${cacheDir}/next.action" ] && \ - [ "$(head -n1 "${cacheDir}/next.action")" == "${backupID}" ] - then - rm -f "${cacheDir}/next.action" "${cacheDir}/next.action2" - fi - ;; - esac -} - -usage() -{ - >&2 echo \ -'Usage: backupStatistics [OPTION] -Search and tidy duplicate and not-hardlinked files in the backups. - -With no options, tidy up all backups. THIS CAN BE VERY TIME CONSUMING. - -Mandatory arguments to long options are mandatory for short options too. - -d, --dummy only generate lists, do not modify backupfiles - -m, --max=maxNum stop execution after step maxNum - -p, --paranoid test for file differences before relinking (test _should_ be obsolete) - -s, --skip=skipNum skip first skipNum steps -#HELPTEXT# # - -the executed steps are:' - - for ((stage=1; stage<=#NUMSTAGES#; stage++)) - do - >&2 echo '' - >&2 echo " ${stage}. $(do_stage ${stage} '##DESCRIBE##')" - done - >&2 echo '' - [ -z "$1" ] && exit 1 - exit $1 -} - -eval set -- "$( - getopt -o dm:ps: \ - --long dummy \ - --long help \ - --long max: \ - --long paranoid \ - --long skip: \ - --long version \ - -n "$(basename "$0")" -- "$@" || \ - echo usage -)" - -dummy=false -maxNum=#NUMSTAGES# -paranoid=false -skipNum=0 - -while true; do - case "$1" in - -d|--dummy) - dummy=true - ;; - --help) - usage 0 - ;; - -m|--max) - shift - maxNum=$1 - ;; - -p|--paranoid) - paranoid=true - ;; - -s|--skip) - shift - skipNum=$1 - ;; - --version) - >&2 echo '#VERSION#' - exit 0 - ;; - --) - shift - [ $# -gt 0 ] && echo 'ERROR: Unknown parameter: '"$#" && usage - break - ;; - *) - >&2 echo 'That should not happen, '"$1"' unknown though ...' - exit -1 - ;; - esac - shift -done - -if [ ! -d "${cacheDir}" ] || [ -z "${cacheDir}" ] -then - >&2 "ERROR: Cache directory must exist, '${cacheDir}' does not! Closing." - exit 1 -fi - -( - echo -n 'Signature: ' - echo -n '.IsCacheDirectory' | \ - md5sum - | \ - cut -d ' ' -f 1 - echo '# This file is a cache directory tag created by '"$(basename "$0")"'.' - echo '# For information about cache directory tags, see:' - echo '# http://www.brynosaurus.com/cachedir/' -) > "${cacheDir}/CACHEDIR.TAG" -( - echo '+ .rsync-filter' - echo '- *' -) > "${cacheDir}/.rsync-filter" - -if [ ! "${skipNum}" -ge 0 ] || \ - [ ! "${skipNum}" -le #NUMSTAGES# ] || \ - [ ! "${maxNum}" -ge 0 ] || \ - [ ! "${maxNum}" -le #NUMSTAGES# ] -then - usage -fi - -for ((stage=${skipNum}+1; stage<=${maxNum}; stage++)) -do - echo "entering stage ${stage} ($(do_stage ${stage} '##DESCRIBE##')) ..." - for backupID in "${!backups[@]}" - do - echo "${backupID}:" - do_stage ${stage} "${backupID}" - done - echo "... stage ${stage} completed." -done diff --git a/check-diff b/check-diff new file mode 100755 index 0000000..8643e04 --- /dev/null +++ b/check-diff @@ -0,0 +1,28 @@ +#!/bin/sh + +a="$1" +aDat="$( + echo "${a}" | \ + tr '/' '\n' | \ + grep -m1 '^[0-9]\{4\}\(_[0-9]\{2\}\)\{2\}$' +)" +dir="$( + echo "${a}" | \ + sed "s|/${aDat}/.*$||" +)" +bDat="$( + ls -1 "${dir}" | \ + grep -v "^${aDat}\$" | \ + grep '^[0-9]\{4\}\(_[0-9]\{2\}\)\{2\}$' | \ + sort | \ + tail -n1 +)" +b="$( + echo "${a}" | \ + sed "s|/2016_12_[0-9][0-9]/|/${bDat}/|" +)" + +(diff -q "$a" "$b" 2> /dev/null > /dev/null) \ + && [ "$(stat "$a" -c'%h')" -lt 64000 ] \ + && [ "$(stat "$b" -c'%h')" -lt 64000 ] \ + && echo "'$a' equals '$b'" diff --git a/checkDiff b/checkDiff deleted file mode 100755 index 8643e04..0000000 --- a/checkDiff +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh - -a="$1" -aDat="$( - echo "${a}" | \ - tr '/' '\n' | \ - grep -m1 '^[0-9]\{4\}\(_[0-9]\{2\}\)\{2\}$' -)" -dir="$( - echo "${a}" | \ - sed "s|/${aDat}/.*$||" -)" -bDat="$( - ls -1 "${dir}" | \ - grep -v "^${aDat}\$" | \ - grep '^[0-9]\{4\}\(_[0-9]\{2\}\)\{2\}$' | \ - sort | \ - tail -n1 -)" -b="$( - echo "${a}" | \ - sed "s|/2016_12_[0-9][0-9]/|/${bDat}/|" -)" - -(diff -q "$a" "$b" 2> /dev/null > /dev/null) \ - && [ "$(stat "$a" -c'%h')" -lt 64000 ] \ - && [ "$(stat "$b" -c'%h')" -lt 64000 ] \ - && echo "'$a' equals '$b'" diff --git a/fast-repair.in b/fast-repair.in new file mode 100644 index 0000000..9d5224a --- /dev/null +++ b/fast-repair.in @@ -0,0 +1,88 @@ +#!/bin/bash + +# fast-repair version #VERSION# + +[ -r "/etc/backup.conf" ] && \ + . "/etc/backup.conf" + +dummy=false +if [ $# -eq 1 ] \ + && [ "$1" == "-d" ] +then + shift + dummy=true +fi + +if [ $# -ne 0 ] +then + >&2 echo "too many arguments: '$@'" + exit 1 +fi + +for backupID in "${!backups[@]}" +do + backupDir="${backups["${backupID}"]%% *}" + lastDate="$( + ls -1 "${backupDir}" | \ + sort -r | \ + grep -m1 '^[0-9]\{4\}\(_[0-9]\{2\}\)\{2\}$' + )" + + dateList=$( + ls -1 "${backupDir}" | \ + sort -r | \ + grep '^[0-9]\{4\}\(_[0-9]\{2\}\)\{2\}$' | \ + grep -v "^${lastDate}" + ) + + [ -z "${dateList}" ] \ + && continue + + set -e + + echo -n "${backupID}: " + + find "${backupDir}${lastDate}" -type f -links 1 | \ + wc -l + + find "${backupDir}${lastDate}" -type f -links 1 | \ + sed "s|^${backupDir}${lastDate}/||" | \ + while read -r datei + do + for date in ${dateList} + do + [ -f "${backupDir}${date}/${datei}" ] \ + || break + diff -q "${backupDir}${lastDate}/${datei}" "${backupDir}${date}/${datei}" > /dev/null \ + || break + nlinks=$( + stat -c'%h' "${backupDir}${date}/${datei}" + ) + [ ${nlinks} -eq 65000 ] \ + && continue + rights=$(stat -c'%a' "${backupDir}${lastDate}/${datei}") + uid=$(stat -c'%u' "${backupDir}${lastDate}/${datei}") + gid=$(stat -c'%g' "${backupDir}${lastDate}/${datei}") + timeModification=$(stat -c'%y' "${backupDir}${lastDate}/${datei}") + + echo rm \"${backupDir}${lastDate}/${datei}\" + echo ln \"${backupDir}${date}/${datei}\" \"${backupDir}${lastDate}/${datei}\" + echo chown ${uid}:${gid} \"${backupDir}${lastDate}/${datei}\" + echo chmod ${rights} \"${backupDir}${lastDate}/${datei}\" + echo touch -m -d \"${timeModification}\" \"${backupDir}${lastDate}/${datei}\" + + if ! ${dummy} + then + rm "${backupDir}${lastDate}/${datei}" + ln "${backupDir}${date}/${datei}" "${backupDir}${lastDate}/${datei}" + chown ${uid}:${gid} "${backupDir}${lastDate}/${datei}" + chmod ${rights} "${backupDir}${lastDate}/${datei}" + touch -m -d "${timeModification}" "${backupDir}${lastDate}/${datei}" + fi + break + done + done + + set +e + +done diff --git a/fastRepair.in b/fastRepair.in deleted file mode 100644 index fb89956..0000000 --- a/fastRepair.in +++ /dev/null @@ -1,88 +0,0 @@ -#!/bin/bash - -# fastRepair version #VERSION# - -[ -r "/etc/backup.conf" ] && \ - . "/etc/backup.conf" - -dummy=false -if [ $# -eq 1 ] \ - && [ "$1" == "-d" ] -then - shift - dummy=true -fi - -if [ $# -ne 0 ] -then - >&2 echo "too many arguments: '$@'" - exit 1 -fi - -for backupID in "${!backups[@]}" -do - backupDir="${backups["${backupID}"]%% *}" - lastDate="$( - ls -1 "${backupDir}" | \ - sort -r | \ - grep -m1 '^[0-9]\{4\}\(_[0-9]\{2\}\)\{2\}$' - )" - - dateList=$( - ls -1 "${backupDir}" | \ - sort -r | \ - grep '^[0-9]\{4\}\(_[0-9]\{2\}\)\{2\}$' | \ - grep -v "^${lastDate}" - ) - - [ -z "${dateList}" ] \ - && continue - - set -e - - echo -n "${backupID}: " - - find "${backupDir}${lastDate}" -type f -links 1 | \ - wc -l - - find "${backupDir}${lastDate}" -type f -links 1 | \ - sed "s|^${backupDir}${lastDate}/||" | \ - while read -r datei - do - for date in ${dateList} - do - [ -f "${backupDir}${date}/${datei}" ] \ - || break - diff -q "${backupDir}${lastDate}/${datei}" "${backupDir}${date}/${datei}" > /dev/null \ - || break - nlinks=$( - stat -c'%h' "${backupDir}${date}/${datei}" - ) - [ ${nlinks} -eq 65000 ] \ - && continue - rights=$(stat -c'%a' "${backupDir}${lastDate}/${datei}") - uid=$(stat -c'%u' "${backupDir}${lastDate}/${datei}") - gid=$(stat -c'%g' "${backupDir}${lastDate}/${datei}") - timeModification=$(stat -c'%y' "${backupDir}${lastDate}/${datei}") - - echo rm \"${backupDir}${lastDate}/${datei}\" - echo ln \"${backupDir}${date}/${datei}\" \"${backupDir}${lastDate}/${datei}\" - echo chown ${uid}:${gid} \"${backupDir}${lastDate}/${datei}\" - echo chmod ${rights} \"${backupDir}${lastDate}/${datei}\" - echo touch -m -d \"${timeModification}\" \"${backupDir}${lastDate}/${datei}\" - - if ! ${dummy} - then - rm "${backupDir}${lastDate}/${datei}" - ln "${backupDir}${date}/${datei}" "${backupDir}${lastDate}/${datei}" - chown ${uid}:${gid} "${backupDir}${lastDate}/${datei}" - chmod ${rights} "${backupDir}${lastDate}/${datei}" - touch -m -d "${timeModification}" "${backupDir}${lastDate}/${datei}" - fi - break - done - done - - set +e - -done diff --git a/last-backups.in b/last-backups.in new file mode 100755 index 0000000..a8ee5f2 --- /dev/null +++ b/last-backups.in @@ -0,0 +1,110 @@ +#!/bin/bash + +# last-backups version #VERSION# + +[ -r "#ETCDIR#/backup.conf" ] && \ + . "#ETCDIR#/backup.conf" + +usage () { + >&2 echo \ +'Usage: last-backups +Show information about date of last backups and warn about outdated ones. + +Options: +#HELPTEXT# #' + [ -n "$1" ] && exit $1 + exit 1 +} + +if [ $# -eq 1 ] +then + if [ "$1" == "--help" ] + then + usage 0 + elif [ "$1" == "--version" ] + then + >&2 echo '#VERSION#' + exit 0 + fi + usage +elif [ $# -gt 1 ] +then + usage +fi + +unset namen +unset veraltet +unset daten + +for backupID in ${!backups[@]} +do + dest="${backups["${backupID}"]%% *}" + dest="${dest%/}" + while [ ! -d "${dest}" ] && [ ${maxWait} -gt 0 ] + do + sleep 1 + maxWait=$[${maxWait}-1] + done + + lbu=$( + ls -1 ${dest} | \ + grep -v "^aktuell$" | \ + sed "s/^duplicity-\(inc\|full\)\.\(.*\.\)\?\([0-9]\{4\}\)\([0-9]\{2\}\)\([0-9]\{2\}\)T[0-9]\{6\}Z\..*$/\3_\4_\5/" | \ + sort -n | \ + uniq | \ + grep "^[0-9]\{4\}_[0-9]\{2\}_[0-9]\{2\}$" | \ + tail -n1 + ) + [ "${lbu}" == "" ] && lbu="2000_01_01" + delta=$[$(date +%s) - $(date -d $(echo ${lbu} | sed "s/_//g") +%s)] + namen[${#namen[@]}]="$(basename $(echo ${dest} | sed "s#/\(${recognSubdirRegex}\)\$#_\1#" | sed "s#/rsync\$##; s#/duplicity\$##"))" + if [ ! -d "${dest}/${lbu}" ] + then + veraltet[${#veraltet[@]}]="LEER!" + elif [ ${delta} -gt ${outdatedLimit} ] && \ + ! printf '%s\n' "${seldomBackups[@]}" | \ + grep -qxF "${backupID}" + then + veraltet[${#veraltet[@]}]="VERALTET!" + elif [ ${delta} -gt ${outdatedSeldomLimit} ] + then + veraltet[${#veraltet[@]}]="VERALTET!" + else + veraltet[${#veraltet[@]}]="" + fi + daten[${#daten[@]}]="${lbu}" +done + +nl=0 +vl=0 +dl=0 + +for ((i=0; i<${#namen[@]}; i++)) +{ + [ ${#namen[${i}]} -gt ${nl} ] && nl=${#namen[${i}]} + [ ${#veraltet[${i}]} -gt ${vl} ] && vl=${#veraltet[${i}]} + [ ${#daten[${i}]} -gt ${dl} ] && dl=${#daten[${i}]} +} + +for ((i=0; i<${#namen[@]}; i++)) +{ + while [ ${#namen[${i}]} -lt ${nl} ] + do + namen[${i}]="${namen[${i}]} " + done + while [ ${#veraltet[${i}]} -lt ${vl} ] + do + veraltet[${i}]="${veraltet[${i}]} " + done + while [ ${#daten[${i}]} -lt ${dl} ] + do + daten[${i}]="${daten[${i}]} " + done +} + +for ((i=0; i<${#namen[@]}; i++)) +{ + echo "${daten[${i}]} ${veraltet[${i}]} ${namen[${i}]} ${daten[${i}]}" +} | \ + sort | \ + sed "s/^\S\+\s//" diff --git a/lastBackups.in b/lastBackups.in deleted file mode 100755 index 9f0d3e8..0000000 --- a/lastBackups.in +++ /dev/null @@ -1,110 +0,0 @@ -#!/bin/bash - -# lastBackups version #VERSION# - -[ -r "#ETCDIR#/backup.conf" ] && \ - . "#ETCDIR#/backup.conf" - -usage () { - >&2 echo \ -'Usage: lastBackups -Show information about date of last backups and warn about outdated ones. - -Options: -#HELPTEXT# #' - [ -n "$1" ] && exit $1 - exit 1 -} - -if [ $# -eq 1 ] -then - if [ "$1" == "--help" ] - then - usage 0 - elif [ "$1" == "--version" ] - then - >&2 echo '#VERSION#' - exit 0 - fi - usage -elif [ $# -gt 1 ] -then - usage -fi - -unset namen -unset veraltet -unset daten - -for backupID in ${!backups[@]} -do - dest="${backups["${backupID}"]%% *}" - dest="${dest%/}" - while [ ! -d "${dest}" ] && [ ${maxWait} -gt 0 ] - do - sleep 1 - maxWait=$[${maxWait}-1] - done - - lbu=$( - ls -1 ${dest} | \ - grep -v "^aktuell$" | \ - sed "s/^duplicity-\(inc\|full\)\.\(.*\.\)\?\([0-9]\{4\}\)\([0-9]\{2\}\)\([0-9]\{2\}\)T[0-9]\{6\}Z\..*$/\3_\4_\5/" | \ - sort -n | \ - uniq | \ - grep "^[0-9]\{4\}_[0-9]\{2\}_[0-9]\{2\}$" | \ - tail -n1 - ) - [ "${lbu}" == "" ] && lbu="2000_01_01" - delta=$[$(date +%s) - $(date -d $(echo ${lbu} | sed "s/_//g") +%s)] - namen[${#namen[@]}]="$(basename $(echo ${dest} | sed "s#/\(${recognSubdirRegex}\)\$#_\1#" | sed "s#/rsync\$##; s#/duplicity\$##"))" - if [ ! -d "${dest}/${lbu}" ] - then - veraltet[${#veraltet[@]}]="LEER!" - elif [ ${delta} -gt ${outdatedLimit} ] && \ - ! printf '%s\n' "${seldomBackups[@]}" | \ - grep -qxF "${backupID}" - then - veraltet[${#veraltet[@]}]="VERALTET!" - elif [ ${delta} -gt ${outdatedSeldomLimit} ] - then - veraltet[${#veraltet[@]}]="VERALTET!" - else - veraltet[${#veraltet[@]}]="" - fi - daten[${#daten[@]}]="${lbu}" -done - -nl=0 -vl=0 -dl=0 - -for ((i=0; i<${#namen[@]}; i++)) -{ - [ ${#namen[${i}]} -gt ${nl} ] && nl=${#namen[${i}]} - [ ${#veraltet[${i}]} -gt ${vl} ] && vl=${#veraltet[${i}]} - [ ${#daten[${i}]} -gt ${dl} ] && dl=${#daten[${i}]} -} - -for ((i=0; i<${#namen[@]}; i++)) -{ - while [ ${#namen[${i}]} -lt ${nl} ] - do - namen[${i}]="${namen[${i}]} " - done - while [ ${#veraltet[${i}]} -lt ${vl} ] - do - veraltet[${i}]="${veraltet[${i}]} " - done - while [ ${#daten[${i}]} -lt ${dl} ] - do - daten[${i}]="${daten[${i}]} " - done -} - -for ((i=0; i<${#namen[@]}; i++)) -{ - echo "${daten[${i}]} ${veraltet[${i}]} ${namen[${i}]} ${daten[${i}]}" -} | \ - sort | \ - sed "s/^\S\+\s//" diff --git a/lastBackups.in.orig b/lastBackups.in.orig deleted file mode 100755 index ea6040a..0000000 --- a/lastBackups.in.orig +++ /dev/null @@ -1,73 +0,0 @@ -#!/bin/bash - -# lastBackups version #VERSION# - -. #ETCDIR#/backup.conf - -unset namen -unset veraltet -unset daten - -for backupID in ${!backups[@]} -do - dest="${backups["${backupID}"]%% *}" - dest="${dest%/}" - while [ ! -d "${dest}" ] && [ ${maxWait} -gt 0 ] - do - sleep 1 - maxWait=$[${maxWait}-1] - done - - lbu=$( - ls -1 ${dest} | \ - grep -v "^aktuell$" | \ - sed "s/^duplicity-\(inc\|full\)\.\(.*\.\)\?\([0-9]\{4\}\)\([0-9]\{2\}\)\([0-9]\{2\}\)T[0-9]\{6\}Z\..*$/\3_\4_\5/" | \ - sort -n | \ - uniq | \ - grep "^[0-9]\{4\}_[0-9]\{2\}_[0-9]\{2\}$" | \ - tail -n1 - ) - [ "${lbu}" == "" ] && lbu="2000_01_01" - delta=$[$(date +%s) - $(date -d $(echo ${lbu} | sed "s/_//g") +%s)] - namen[${#namen[@]}]="$(basename $(echo ${dest} | sed "s#/\(${recognSubdirRegex}\)\$#_\1#" | sed "s#/rsync\$##; s#/duplicity\$##"))" - [ ! "$(ls -A ${dest}/${lbu})" ] && - veraltet[${#veraltet[@]}]="LEER!" || - [ ${delta} -gt ${outdatedLimit} ] && \ - veraltet[${#veraltet[@]}]="VERALTET!" || \ - veraltet[${#veraltet[@]}]="" - daten[${#daten[@]}]="${lbu}" -done - -nl=0 -vl=0 -dl=0 - -for ((i=0; i<${#namen[@]}; i++)) -{ - [ ${#namen[${i}]} -gt ${nl} ] && nl=${#namen[${i}]} - [ ${#veraltet[${i}]} -gt ${vl} ] && vl=${#veraltet[${i}]} - [ ${#daten[${i}]} -gt ${dl} ] && dl=${#daten[${i}]} -} - -for ((i=0; i<${#namen[@]}; i++)) -{ - while [ ${#namen[${i}]} -lt ${nl} ] - do - namen[${i}]="${namen[${i}]} " - done - while [ ${#veraltet[${i}]} -lt ${vl} ] - do - veraltet[${i}]="${veraltet[${i}]} " - done - while [ ${#daten[${i}]} -lt ${dl} ] - do - daten[${i}]="${daten[${i}]} " - done -} - -for ((i=0; i<${#namen[@]}; i++)) -{ - echo "${daten[${i}]} ${veraltet[${i}]} ${namen[${i}]} ${daten[${i}]}" -} | \ - sort | \ - sed "s/^\S\+\s//" diff --git a/man.commons.in b/man.commons.in index 7227c2e..4ebb720 100644 --- a/man.commons.in +++ b/man.commons.in @@ -4,8 +4,9 @@ Erich Eckner . [SEE ALSO] backup(1) -lastBackups(1) -backupStatistics(1) +backup-statistics(1) +last-backups(1) +remove-old-backups(1) [CONFIG] The configfile \fB#ETCDIR#/backup.conf\fP is a bash script, which defines the following variables: .TP @@ -16,13 +17,13 @@ array of paths to exclude from backup array with command line arguments for predefined backups for \fBbackup\fP .TP .B "maxWait" -maximum time to wait for destination directories to appear in \fBlastBackups\fP and \fBbackupStatistics\fP [seconds] +maximum time to wait for destination directories to appear in \fBlast-backups\fP and \fBbackup-statistics\fP [seconds] .TP .B "outdatedLimit" -time before backups are considered outdated by \fBlastBackups\fP [seconds] +time before backups are considered outdated by \fBlast-backups\fP [seconds] .TP .B "recognSubdirRegex" -regular expression of subdirectories which should be appended to the parent directory in the report by \fBlastBackups\fP +regular expression of subdirectories which should be appended to the parent directory in the report by \fBlast-backups\fP .TP .B "cacheDir" -directory to store valuable information generated by \fBbackupStatistics\fP in +directory to store valuable information generated by \fBbackup-statistics\fP in diff --git a/remove-old-backups.in b/remove-old-backups.in new file mode 100755 index 0000000..c931409 --- /dev/null +++ b/remove-old-backups.in @@ -0,0 +1,79 @@ +#!/bin/bash + +# remove-old-backups version #VERSION# + +[ -r "#ETCDIR#/backup.conf" ] && \ + . "#ETCDIR#/backup.conf" + +usage () { + >&2 echo \ +'Usage: remove-old-backups [-n] +Remove backups older than one year, keeping only one backup per month. + +Options: +#HELPTEXT# # + -n only print what would be removed' + [ -n "$1" ] && exit $1 + exit 1 +} + +no_action=false + +if [ $# -eq 1 ] +then + if [ "x$1" == 'x--help' ] + then + usage 0 + elif [ "x$1" == 'x--version' ] + then + >&2 echo '#VERSION#' + exit 0 + elif [ "x$1" == 'x-n' ] + then + no_action=true + else + usage + fi +elif [ $# -gt 1 ] +then + usage +fi + +backups=$( + printf '%s\n' "${backups[@]}" | \ + cut -d' ' -f1 | \ + while read -r dir; do \ + if [ $(ls "$dir" | wc -l) -lt 30 ]; then + continue; + fi + ls -1 "$dir" | \ + sed -n ' + s,^\([0-9]\{4\}\)_\([0-9]\{2\}\)_\([0-9]\{2\}\)$,'"${dir}"'\0 \3 \2 \1 '"${dir}"', + T + p + ' + done +) + +{ + printf '%s\n' "${backups}" | \ + sort -k5,5 -k4,4 -k3,3 -k2,2 -k1,1 | \ + uniq -f2 + printf '%s\n' "${backups}" | \ + sed "$( + for dist in $(seq 0 20 365); do + printf '/ %s \S\+$/p\n' \ + "$(date '+%m %Y' -d@$(($(date +%s)-24*60*60*${dist})))" + done | \ + sort -u + )" +} | \ + cut -d' ' -f1 | \ + sort | \ + uniq -u | \ + while read -r dir; do + echo rm -rf --one-file-system "${dir}" + if ! ${no_action}; then + rm -rf --one-file-system "${dir}" + fi + done diff --git a/removeOldBackups.in b/removeOldBackups.in deleted file mode 100755 index 145a6fd..0000000 --- a/removeOldBackups.in +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/bash - -# removeOldBackups version #VERSION# - -[ -r "#ETCDIR#/backup.conf" ] && \ - . "#ETCDIR#/backup.conf" - -usage () { - >&2 echo \ -'Usage: removeOldBackups [-n] -Remove backups older than one year, keeping only one backup per month. - -Options: -#HELPTEXT# # - -n only print what would be removed' - [ -n "$1" ] && exit $1 - exit 1 -} - -no_action=false - -if [ $# -eq 1 ] -then - if [ "x$1" == 'x--help' ] - then - usage 0 - elif [ "x$1" == 'x--version' ] - then - >&2 echo '#VERSION#' - exit 0 - elif [ "x$1" == 'x-n' ] - then - no_action=true - else - usage - fi -elif [ $# -gt 1 ] -then - usage -fi - -backups=$( - printf '%s\n' "${backups[@]}" | \ - cut -d' ' -f1 | \ - while read -r dir; do \ - if [ $(ls "$dir" | wc -l) -lt 30 ]; then - continue; - fi - ls -1 "$dir" | \ - sed -n ' - s,^\([0-9]\{4\}\)_\([0-9]\{2\}\)_\([0-9]\{2\}\)$,'"${dir}"'\0 \3 \2 \1 '"${dir}"', - T - p - ' - done -) - -{ - printf '%s\n' "${backups}" | \ - sort -k5,5 -k4,4 -k3,3 -k2,2 -k1,1 | \ - uniq -f2 - printf '%s\n' "${backups}" | \ - sed "$( - for dist in $(seq 0 20 365); do - printf '/ %s \S\+$/p\n' \ - "$(date '+%m %Y' -d@$(($(date +%s)-24*60*60*${dist})))" - done | \ - sort -u - )" -} | \ - cut -d' ' -f1 | \ - sort | \ - uniq -u | \ - while read -r dir; do - echo rm -rf --one-file-system "${dir}" - if ! ${no_action}; then - rm -rf --one-file-system "${dir}" - fi - done -- cgit v1.2.3