Attributable to nature of how TimeMachine works, every iteration of a backup is a sparse bundle (not only a folder on TM disk) and a few unlucky modifications in library could unfastened years of your music recollections.
I got here up with a script shopping by means of all iterations of your TM backup and rsync’ing it to new vacation spot. Realized so much throughout this train. Please be at liberty to critique and advise enhancements!
#!/bin/bash set -euo pipefail # ======= YOUR PATH GOES HERE ======= SRC_VOL="/Volumes/Backup" DEST="/Volumes/NewHDD" USERNAME="ppervoy" # Folders inside snapshots to revover REL_PATHS=( "Macintosh HDD/Customers/$USERNAME/Music" "Macintosh HDD/Customers/$USERNAME/Photos" ) # ========================== # Resolve the system node for the APFS quantity DEV="$(diskutil data "$SRC_VOL" | awk -F': *' '/Gadget Node/ {print $2}')" if [[ -z "${DEV:-}" ]]; then echo "ERROR: No system node for $SRC_VOL" exit 1 fi # Get snapshot names SNAPS=() whereas IFS= learn -r line; do title=$(echo "$line" | sed -n 's/.*Identify:[[:space:]]*//p') [[ -n "$name" ]] && SNAPS+=("$title") completed >> Discovered ${#SNAPS[@]} snapshots on $SRC_VOL:" i=1 for snap in "${SNAPS[@]}"; do printf " %second) %sn" "$i" "$snap" ((i++)) completed echo learn -r -p "Course of ALL snapshots for a union copy? [Y/n]: " yn if [[ "${yn:-Y}" =~ ^[Nn]$ ]]; then learn -r -p "Enter a single snapshot quantity to course of: " idx if ! [[ "$idx" =~ ^[0-9]+$ ]] || (( idx ${#SNAPS[@]} )); then echo "Invalid choice."; exit 1 fi SNAPS=("${SNAPS[$((idx-1))]}") fi mkdir -p "$DEST" for SNAP in "${SNAPS[@]}"; do DATE="${SNAP##*.TimeMachine.}"; DATE="${DATE%.backup}" MNT="/Volumes/TMSnap_${DATE}" echo ">>> Mounting snapshot: $SNAP -> $MNT" sudo mkdir -p "$MNT" if mount | grep -q "on $MNT "; then sudo diskutil unmount power "$MNT" >/dev/null 2>&1 || true fi if ! sudo mount_apfs -s "$SNAP" "$DEV" "$MNT"; then echo "!!! Did not mount $SNAP... Skipping." sudo diskutil unmount power "$MNT" >/dev/null 2>&1 || true rmdir "$MNT" 2>/dev/null || true proceed fi SRC="" for REL in "${REL_PATHS[@]}"; do CAND="$MNT/$DATE.backup/$REL" if [[ -d "$CAND" ]]; then SRC="$CAND"; break fi completed if [[ -z "$SRC" ]]; then echo "??? Path not present in $SNAP. Tried:" for REL in "${REL_PATHS[@]}"; do echo " $MNT/$DATE/$REL" completed else echo ">>> rsync from: $SRC" echo ">>> rsync to: $DEST" # rsync -av --ignore-existing "$SRC/" "$DEST/" fi echo ">>> Unmounting $MNT" sudo diskutil unmount power "$MNT" >/dev/null || true rmdir "$MNT" 2>/dev/null || true completed echo "Carried out. Union copy full at: $DEST"
Strive working it dry, than uncomment rsync. If permissions after look odd – strive sudo chown -R $(whoami):workers "$DEST")"