Skip to content

Commit

Permalink
Merge branch 'release/1.1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
Enrico M. Crisostomo committed Oct 21, 2016
2 parents c48d60c + dff99a6 commit a536c11
Showing 1 changed file with 137 additions and 73 deletions.
210 changes: 137 additions & 73 deletions tm-cleanup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ set -o errexit
set -o nounset

PROGNAME=$0
VERSION=1.1.0

command -v tmutil > /dev/null 2>&1 || {
>&2 print -- Cannot find tmutil.
Expand All @@ -32,63 +33,154 @@ command -v tmutil > /dev/null 2>&1 || {

print_usage()
{
print -- "${PROGNAME}"
print -- "${PROGNAME} ${VERSION}"
print
print -- "Usage:"
print -- "${PROGNAME} [-d days] [-f] [-x]"
print -- "${PROGNAME} [-d days] [-n number] [-f] [-x]"
print -- "${PROGNAME} [-h]"
print
print -- "Options:"
print -- " -d Number of days to keep."
print -- " -f Force execution even if a Time Machine backup is in progress."
print -- " -h Show this help."
print -- " -n Number of backups to keep."
print -- " -x Perform a dry run."
print
print -- "Report bugs to <enrico.m.crisostomo@gmail.com>."
print -- "Report bugs to <https://github.com/emcrisostomo/Time-Machine-Cleanup>."
}

# Define an integer variable to store the deletion threshold.
# Define an integer variable to store the deletion threshold for each mode.
# Default: 30 days
# Default: 7 backups
typeset -i DAYS_TO_KEEP=30
typeset -i NUMBER_TO_KEEP=7
DRY_RUN=0
FORCE_EXECUTION=0
# Execution modes
# - 0: number of days
# - 1: number of backups
MODE_DAYS=0
MODE_BACKUPS=1
typeset -i EXECUTION_MODE=-1
typeset -i ARGS_PROCESSED=0

parse_opts()
{
while getopts ":hd:fn:x" opt
do
case $opt in
h)
print_usage
exit 0
;;
d)
DAYS_TO_KEEP=${OPTARG}
EXECUTION_MODE=${MODE_DAYS}
;;
f)
FORCE_EXECUTION=1
;;
n)
NUMBER_TO_KEEP=${OPTARG}
EXECUTION_MODE=${MODE_BACKUPS}
;;
x)
DRY_RUN=1
;;
\?)
>&2 print -- Invalid option -${OPTARG}.
exit 1
;;
:)
>&2 print -- Missing argument to -${OPTARG}.
exit 1
;;
esac
done

ARGS_PROCESSED=$((OPTIND - 1))
}

while getopts ":hd:fx" opt
do
case $opt in
h)
print_usage
exit 0
;;
d)
DAYS_TO_KEEP=${OPTARG}
;;
f)
FORCE_EXECUTION=1
;;
x)
DRY_RUN=1
;;
\?)
>&2 print -- Invalid option -${OPTARG}.
exit 1
;;
:)
>&2 print -- Missing argument to -${OPTARG}.
exit 1
;;
esac
done

shift $((OPTIND-1))
process_by_days()
{
(( ${DAYS_TO_KEEP} > 0 )) || {
>&2 print -- The number of days to keep must be positive.
exit 2
}

(( $# == 0 )) || {
>&2 print -- No arguments are allowed.
exit 2
# Establish the threshold date before which backups will be deleted
THRESHOLD_DATE=$(date -j -v-${DAYS_TO_KEEP}d +"%Y-%m-%d")

# As a safety precaution, just check that the output format has not changed.
# If it has, let's not proceed.
for i in ${TM_BACKUPS_SORTED}
do
TM_DATE=$(basename $i)

if [[ ! ${TM_DATE} =~ "^[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{6}$" ]]
then
>&2 print -- Unexpected snapshot name: ${TM_DATE}.
>&2 print -- Aborting.
exit 8
fi
done

for i in ${TM_BACKUPS_SORTED}
do
TM_DATE=$(basename $i)

if [[ ${THRESHOLD_DATE} > ${TM_DATE} ]]
then
if [[ ${i} != ${TM_BACKUPS_SORTED[-1]} ]]
then
print -- ${TM_DATE} will be deleted.

if (( ${DRY_RUN} == 0 ))
then
tmutil delete ${i}
fi
else
print -- ${TM_DATE} will not be deleted because it is the latest available Time Machine snapshot.
fi
fi
done
}

(( ${DAYS_TO_KEEP} > 0 )) || {
>&2 print -- The number of days to keep must be positive.
process_by_backups()
{
(( ${NUMBER_TO_KEEP} > 0 )) || {
>&2 print -- The number of backups to keep must be positive.
exit 2
}

if (( ${NUMBER_TO_KEEP} >= ${#TM_BACKUPS_SORTED} ))
then
exit 0
fi

typeset -i LAST_IDX=$(( ${#TM_BACKUPS_SORTED} - ${NUMBER_TO_KEEP} ))

for i in $(seq 1 ${LAST_IDX})
do
if [[ ${i} != ${TM_BACKUPS_SORTED[-1]} ]]
then
print -- ${TM_BACKUPS_SORTED[i]:t} will be deleted.

if (( ${DRY_RUN} == 0 ))
then
tmutil delete ${TM_BACKUPS_SORTED[i]}
fi
else
print -- ${TM_DATE} will not be deleted because it is the latest available Time Machine snapshot.
fi
done
}

# Main
parse_opts $* && shift ${ARGS_PROCESSED}

(( $# == 0 )) || {
>&2 print -- No arguments are allowed.
exit 2
}

Expand All @@ -112,39 +204,11 @@ TM_BACKUPS=( "${(ps:\n:)$(tmutil listbackups)}" )
# states nowhere that the output is sorted in any way.
TM_BACKUPS_SORTED=( ${(n)TM_BACKUPS} )

# Establish the threshold date before which backups will be deleted
THRESHOLD_DATE=$(date -j -v-${DAYS_TO_KEEP}d +"%Y-%m-%d")

# As a safety precaution, just check that the output format has not changed.
# If it has, let's not proceed.
for i in ${TM_BACKUPS_SORTED}
do
TM_DATE=$(basename $i)

if [[ ! ${TM_DATE} =~ "^[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{6}$" ]]
then
>&2 print -- Unexpected snapshot name: ${TM_DATE}.
>&2 print -- Aborting.
exit 8
fi
done

for i in ${TM_BACKUPS_SORTED}
do
TM_DATE=$(basename $i)

if [[ ${THRESHOLD_DATE} > ${TM_DATE} ]]
then
if [[ ${i} != ${TM_BACKUPS_SORTED[-1]} ]]
then
print -- ${TM_DATE} will be deleted.

if (( ${DRY_RUN} == 0 ))
then
tmutil delete ${i}
fi
else
print -- ${TM_DATE} will not be deleted because it is the latest available Time Machine snapshot.
fi
fi
done
case ${EXECUTION_MODE} in
${MODE_DAYS})
process_by_days
;;
${MODE_BACKUPS})
process_by_backups
;;
esac

0 comments on commit a536c11

Please sign in to comment.