Skip to content

Instantly share code, notes, and snippets.

@libcrack
Created May 17, 2024 12:00
Show Gist options
  • Save libcrack/8bb4aa2668f67dc2c411ee2f7322dea6 to your computer and use it in GitHub Desktop.
Save libcrack/8bb4aa2668f67dc2c411ee2f7322dea6 to your computer and use it in GitHub Desktop.
macOS timemachine bash helper functions
# Fri May 17 13:52:40 CEST 2024
# devnull@libcrack.so
#
# timemachine helper functions:
# timemachine-notify
# timemachine-notify-loop
# timemachine-speedup
# timemachine-log
# timemachine-logstream
# timemachine-log-show
# timemachine-log-stream
# timemachine-dialog
#
# Usage:
# source timemachine.sh
#
# Note: for timemachine-dialog to work you need to install dialog:
# brew install dialog
#
# ——————————————————————————————————————————————————————————————————————————————
# msg
# ——————————————————————————————————————————————————————————————————————————————
msg(){
[[ ! -n "${1}" ]] && { msg="${_}"; } || { msg="${*}"; }
printf "\e[1m[➤]\e[0m ${msg}\n" > /dev/stdout
return $?
}
# ——————————————————————————————————————————————————————————————————————————————
# error
# ——————————————————————————————————————————————————————————————————————————————
error(){
[[ ! -n "${1}" ]] && { msg="${_}"; } || { msg="${*}"; }
printf "${RED}[✕]${RESET} ${msg}\n" > /dev/stderr;
return $?
}
# ——————————————————————————————————————————————————————————————————————————————
# success
# ——————————————————————————————————————————————————————————————————————————————
success(){
[[ ! -n "${1}" ]] && { msg="${_}"; } || { msg="${*}"; }
printf "${IGREEN}[✓]${RESET} ${msg}\n" > /dev/stdout;
return $?
}
# ——————————————————————————————————————————————————————————————————————————————
# timemachine-notify-loop
# ——————————————————————————————————————————————————————————————————————————————
timemachine-notify(){
/usr/bin/tmutil status | /usr/bin/grep -q "Running = 0;" && {
error "TimeMachine Backup is not running (1)" > /dev/stderr
return 1
}
/usr/bin/tmutil status | /usr/bin/grep -q "Running = 1;" || {
error "TimeMachine Backup is not running (2)" > /dev/stderr
return 2
}
# tmutil status
# Backup session status:
# {
# ClientID = "com.apple.backupd";
# Running = 0;
# }
# -----------------------------------
# BackupPhase = FindingChanges;
# ClientID = "com.apple.backupd";
# DateOfStateChange = "2023-03-26 03:31:30 +0000";
# DestinationID = "DACD0E28-5A80-46BB-9A1E-4F246FB53B8B";
# DestinationMountPoint = "/Volumes/Time Machine Backups";
# Percent = "-1";
# Running = 1;
# Stopping = 0;
# -----------------------------------
# BackupPhase = SizingChanges;
# ClientID = "com.apple.backupd";
# DateOfStateChange = "2023-03-26 04:44:46 +0000";
# DestinationID = "DACD0E28-5A80-46BB-9A1E-4F246FB53B8B";
# DestinationMountPoint = "/Volumes/Time Machine Backups";
# Percent = "-1";
# Running = 1;
# Stopping = 0;
# -----------------------------------
# Backup session status:
# BackupPhase = PreparingSourceVolumes;
# ClientID = "com.apple.backupd";
# DateOfStateChange = "2023-04-11 19:07:54 +0000";
# DestinationID = "284420B7-08AE-48B4-8330-81D4456263E1";
# DestinationMountPoint = "/Volumes/SSD";
# FirstBackup = 1;
# Percent = "-1";
# Running = 1;
# Stopping = 0;
# -----------------------------------
# BackupPhase = MountingBackupVol;
# Percent = "-1";
# Running = 1;
# Stopping = 0;
# -----------------------------------
# BackupPhase = Starting;
# Percent = "-1";
# Running = 1;
# Stopping = 0;
# -----------------------------------
# BackupPhase = ThinningPreBackup;
# Percent = "-1";
# Running = 1;
# Stopping = 0;
# -----------------------------------
# BackupPhase = ThinningPostBackup;
# Percent = "0.8174361601788563";
# Running = 1;
# Stopping = 0;
# -----------------------------------
# BackupPhase = HealthCheckFsck;
# ClientID = "com.apple.backupd";
# DateOfStateChange = "2022-10-31 00:04:22 +0000";
# DestinationID = "04C75A92-CDBE-440A-B16D-55263971730E";
# Percent = "0.78";
# Running = 1;
# Stopping = 0;
# -----------------------------------
# BackupPhase = Copying;
# Progress = {
# Percent = "0.984929769770861";
# TimeRemaining = 54;
# "_raw_Percent" = "0.984929769770861";
# "_raw_totalBytes" = 4614522535;
# bytes = 4544980618;
# files = 118548;
# totalBytes = 4614522535;
# totalFiles = 98774;
# };
# Running = 1;
# Stopping = 0;
# -----------------------------------
# bytes = 1616504327;
# totalBytes = 10757451460;
# -----------------------------------
# Progress = {
# Percent = 1;
# "_raw_Percent" = "1.07504395858368";
# "_raw_totalBytes" = 4614522535;
# bytes = 4960814573;
# files = 277411;
# totalBytes = 4614522535;
# totalFiles = 257637;
# }
# -----------------------------------
# -----------------------------------
# -----------------------------------
local phase="$(/usr/bin/tmutil status | /usr/bin/grep BackupPhase | awk '{print $3}' | tr -d ';')"
case "${phase}" in
Copying|ThinningPostBackup)
;;
Starting|PreparingSourceVolumes|HealthCheckFsck|MountingBackupVol|FindingChanges|SizingChanges|ThinningPreBackup)
alert "Yet not copying files. Current phase: \"${phase}\"\n"
return 10
;;
*)
error "No phase detected: \"${phase}\"\n"
#printf "Error: No phase detected: \"${phase}\"\n"
return 5
;;
esac
# case "${phase}" in
# "Starting") ;;
# "PreparingSourceVolumes") alert "Current phase: \"${phase}\"\n"; return 10; ;;
# "HealthCheckFsck") ;;
# "MountingBackupVol") ;;
# "FindingChanges") ;;
# "SizingChanges") ;;
# "ThinningPreBackup") ;;
# "ThinningPostBackup") ;;
# "Copying") ;;
# *)
# error "No phase detected: \"${phase}\"\n"
# #printf "Error: No phase detected: \"${phase}\"\n"
# return 5
# ;;
# esac
local threshold=60
local percent="$(/usr/bin/tmutil status | /usr/bin/grep 'Percent = ' | awk '{print $3}' | tr -d ';' | tr -d '"')"
local remainig_secs="$(/usr/bin/tmutil status | /usr/bin/grep TimeRemaining | awk '{print $3}' | tr -d ';' | tr -d '"')"
# [[ -z "${remainig_secs}" ]] && remainig_secs=0
local remainig_minutes="$(echo "${remainig_secs}/60" | /usr/bin/bc -l)"
local remainig_hours="$(echo "${remainig_secs}/3600" | /usr/bin/bc -l)"
local running="$(/usr/bin/tmutil status | /usr/bin/grep Running | awk '{print $3}' | tr -d ';')"
local stopping="$(/usr/bin/tmutil status | /usr/bin/grep Stopping | awk '{print $3}' | tr -d ';')"
local bytes="$(/usr/bin/tmutil status | /usr/bin/grep 'bytes = ' | awk '{print $3}' | tr -d ';')"
local totalBytes="$(/usr/bin/tmutil status | /usr/bin/grep 'totalBytes = ' | awk '{print $3}' | tr -d ';')"
local Mbytes=$((${bytes}/1024/1024))
# .bashrc: line 1240: /1024/1024: syntax error: operand expected (error token is "/1024/1024")
local totalMBytes=$((${totalBytes}/1024/1024))
local remaining_mbytes="$((${totalMBytes}-${Mbytes}))"
local total_percent="$(echo ${percent}*100 | bc -l)"
printf "Running: ${running}\n"
printf "Stopping: ${stopping}\n"
printf "BackupPhase: ${phase}\n"
# printf "Percent: ${percent}\n"
printf "Percent: \e[1m%.2f %%\e[0m\n" "${total_percent}"
# printf "TimeRemaining: ${remainig_secs} secs\n"
printf "Remaining Seconds: \e[1m%d secs\e[0m\n" "${remainig_secs}"
printf "Remaining Minutes: \e[1m%.2f mins\e[0m\n" "${remainig_minutes}"
printf "Remaining Hours: \e[1m%.2f hours\e[0m\n" "${remainig_hours}"
#printf "bytes: ${bytes}\n"
#printf "total bytes: ${totalBytes}\n"
printf "Megabytes: ${Mbytes} MB\n"
printf "Total Megabytes: ${totalMBytes} MB\n"
printf "Remaining Megabytes: \e[1m${remaining_mbytes} MB\e[0m\n"
[[ -z "${remainig_secs}" ]] && {
error "TimeMachine Backup running but no TimeRemaining (3)" > /dev/stderr
/usr/bin/tmutil status
return 3
}
[[ ${remainig_secs} -le ${threshold} ]] && {
msg "${remainig_secs} < ${threshold}"
terminal-notifier \
-title "TimeMachine Backup" \
-message "Backup finish in less than a minute (${remainig_secs} secs)"
}
return ${remainig_secs}
return $?
}
# ——————————————————————————————————————————————————————————————————————————————
# timemachine-notify-loop
# ——————————————————————————————————————————————————————————————————————————————
timemachine-notify-loop(){
local interval=$((60*5))
# local message="$(timemachine-notify \
# | grep -E '^(Percent|Remaining (Minutes|Hours))' \
# | sed -e 's/\x1B\[[0-9;]\{1,\}[A-Za-z]//g')"
[[ -n "${1}" ]] && interval="${1}"
while true; do
local message="$(timemachine-notify \
| grep -E '^(Percent|Remaining (Minutes|Hours))' \
| sed -e 's/\x1B\[[0-9;]\{1,\}[A-Za-z]//g')"
notifier-terminal \
-title "Time Machine Backup" \
-message "${message}"
sleep ${interval}
done
return $?
}
# ——————————————————————————————————————————————————————————————————————————————
# timemachine-notify-loop
# ——————————————————————————————————————————————————————————————————————————————
timemachine-speedup(){
/usr/bin/tmutil status | /usr/bin/grep -q "Running = 1;" && {
warning "TimeMachine Backup is running" > /dev/stdout
msg "Disabling debug.lowpri_throttle_enabled=0" > /dev/stdout
sudo sysctl debug.lowpri_throttle_enabled=0
msg "Increasing task priority renice -20" > /dev/stdout
sudo renice -20 "$(pgrep '^backupd$')"
}
# } || {
/usr/bin/tmutil status | /usr/bin/grep -q "Running = 0;" && {
warning "TimeMachine Backup is not running" > /dev/stdout
msg "Enabling debug.lowpri_throttle_enabled=1" > /dev/stdout
sudo sysctl debug.lowpri_throttle_enabled=1
}
return $?
}
# ——————————————————————————————————————————————————————————————————————————————
# timemachine-log
# ——————————————————————————————————————————————————————————————————————————————
timemachine-log(){
/usr/bin/log show \
--predicate 'subsystem == "com.apple.TimeMachine"' \
--style compact \
--last 1h \
--info \
--debug
# --info | grep 'upd: (' | cut -c 1-19,87-999
return $?
}
# ——————————————————————————————————————————————————————————————————————————————
# timemachine-logstream
# ——————————————————————————————————————————————————————————————————————————————
timemachine-logstream(){
/usr/bin/log stream \
--predicate 'subsystem == "com.apple.TimeMachine"' \
--style compact \
--level debug
return $?
}
# ——————————————————————————————————————————————————————————————————————————————
# timemachine-log-show
# ——————————————————————————————————————————————————————————————————————————————
timemachine-log-show(){
/usr/bin/log show \
--predicate "(process == \"backupd\") || (process == \"backupd-helper\")" \
--debug
return $?
}
# ——————————————————————————————————————————————————————————————————————————————
# timemachine-log-stream
# ——————————————————————————————————————————————————————————————————————————————
timemachine-log-stream(){
/usr/bin/log stream \
--predicate "(process == \"backupd\") || (process == \"backupd-helper\")" \
--level debug
return $?
}
# ——————————————————————————————————————————————————————————————————————————————
# timemachine-dialog
# XXX brew install dialog
# ——————————————————————————————————————————————————————————————————————————————
timemachine-dialog(){
while true; do
echo "$(tmutil status | grep "Percent =" | awk "{print \$3}" | cut -f2 -d \")*100" | bc -l | cut -f1 -d.
sleep 5
done | dialog --guage "TimeMachine Backup" 6 $((COLUMNS-20))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment