I have a ZFS send script that generates bookmarks because it will use the last bookmark to continue send/recv operations should I need to delete all snapshots. Anyway, I found some datasets have 88 bookmarks, other 70 bookmarks. I think there are no performance issues, only the bookmark list command may drag.
Anyway, this script keeps the last 14 bookmarks. It finds all datasets under the given path.
Posted here if someone finds it useful in the future. It has a build-in cheeky --dry-run flag too.
#!/bin/bash
PARENT_DATASET="pool/dataset" # This script will list all datasets
KEEP_COUNT=7
DRY_RUN=0
# Parse arguments for --dry-run
for arg in "$@"; do
if [[ "$arg" == "--dry-run" ]]; then
DRY_RUN=1
fi
done
echo "Cleaning bookmarks under $PARENT_DATASET, keeping the last $KEEP_COUNT bookmarks per dataset..."
if [[ $DRY_RUN -eq 1 ]]; then
echo ">>> DRY RUN MODE - No deletions will be performed <<<"
fi
# Get all datasets
DATASETS=$(sudo zfs list -H -o name | grep "^${PARENT_DATASET}")
for DATASET in $DATASETS; do
echo "Processing dataset: $DATASET"
BOOKMARKS=$(sudo zfs list -t bookmark -o name -H | grep "^${DATASET}#" | sort)
COUNT=$(echo "$BOOKMARKS" | wc -l)
if (( COUNT <= KEEP_COUNT )); then
echo " Only $COUNT bookmarks found, nothing to delete."
continue
fi
DELETE_COUNT=$(( COUNT - KEEP_COUNT ))
echo " Found $COUNT bookmarks, deleting $DELETE_COUNT oldest..."
echo "$BOOKMARKS" | head -n "$DELETE_COUNT" | while read -r BM; do
if [[ $DRY_RUN -eq 1 ]]; then
echo "Would delete: $BM"
else
echo "Deleting bookmark: $BM"
sudo zfs destroy "$BM"
fi
done
done
echo "Bookmark cleanup completed."