prune-roots.sh (3919B)
1 argv0="prune-roots" 2 3 help() { 4 cat <<EOF 5 $argv0 - Prune non-current root filesystems. 6 7 Usage: $argv0 [options...] 8 9 Options: 10 -k, --keep=n 11 Keep the latest n roots. 12 13 -b, --before=date 14 Prune roots from before date. 15 16 -y, --yes 17 Delete filesystems without confirmation. 18 19 -n, --dry-run 20 List filesystems that would be deleted. 21 22 -l, --list 23 List all non-current filesystems along with their creation times. 24 25 -h, --help 26 Display this message. 27 28 If both --keep and --before are specified, a filesystem will need to 29 match both filters to be selected for deletion. 30 EOF 31 } 32 33 list() { 34 local filesystems=("$@") 35 ( 36 printf "AGE NAME CREATION\n" 37 for i in "${!filesystems[@]}"; do 38 age="$((${#filesystems[@]}-$i))" 39 fs="${filesystems[$i]}" 40 creation="$(date -d "@$(zfs get -Hpo value creation "$fs")")" 41 printf "%d %s %s\n" "$age" "$fs" "$creation" 42 done 43 ) | column -tl3 44 } 45 46 keep="" 47 before="" 48 confirmation="" 49 dry="" 50 51 readarray -t filesystems < <(\ 52 zfs list -t filesystem -Ho name -s creation \ 53 | awk '/^system\/root\// && !/^system\/root\/current$/' \ 54 ) 55 56 while [ $# -gt 0 ]; do 57 case "$1" in 58 -k*|--keep|--keep=*) 59 keep="${1/#-k/}" 60 if [ -z "$keep" ] || [ "$keep" = "$1" ]; then 61 keep="${1#*=}" 62 fi 63 if [ -z "$keep" ] || [ "$keep" = "$1" ]; then 64 if [ $# -lt 2 ]; then 65 printf "Error: Flag needs an argument: %s\n" "$1" >&2 66 exit 1 67 fi 68 keep="$2" 69 shift 70 fi 71 shift 72 if [ ! "$keep" -ge 0 ]; then 73 printf "Error: Invalid value for --keep: Must be an unsigned integer.\n" >&2 74 exit 1 75 fi 76 ;; 77 -b*|--before*) 78 before="${1/#-b/}" 79 if [ -z "$before" ] || [ "$before" = "$1" ]; then 80 before="${1#*=}" 81 fi 82 if [ -z "$before" ] || [ "$before" = "$1" ]; then 83 if [ $# -lt 2 ]; then 84 printf "Error: Flag needs an argument: %s\n" "$1" >&2 85 exit 1 86 fi 87 before="$2" 88 shift 89 fi 90 shift 91 if date -d "$before" 2>/dev/null >/dev/null; then 92 before="$(date -d "$before" +%s)" 93 else 94 error="$(date -d "$before" 2>&1)" 95 error="${error#date: }" 96 printf "Error: Invalid value for --before: %s\n" "${error^}" >&2 97 exit 1 98 fi 99 ;; 100 -h*|--help*) 101 help 102 exit 0 103 ;; 104 -y|--yes) 105 confirmation="Yes" 106 shift 107 ;; 108 -n|--dry-run) 109 dry=1 110 shift 111 ;; 112 -l|--list) 113 list "${filesystems[@]}" 114 exit 0 115 ;; 116 *) 117 printf "Error: Unknown argument: %s\n" "$1" >&2 118 exit 1 119 esac 120 done 121 122 delete=() 123 for i in "${!filesystems[@]}"; do 124 fs="${filesystems[$i]}" 125 creation="$(zfs get -Hpo value creation "$fs")" 126 if [ ! -z "$before" ] && [ "$creation" -ge "$before" ]; then 127 continue 128 fi 129 if [ ! -z "$keep" ] && [ $((${#filesystems[@]}-$i)) -le "$keep" ]; then 130 continue 131 fi 132 delete+=($fs) 133 done 134 135 if [ ${#delete[@]} -le 0 ]; then 136 printf "Nothing to do: No matching filesystems.\n" >&2 137 exit 0 138 fi 139 140 list "${delete[@]}" 141 142 if [ ! -z "$dry" ]; then 143 exit 0 144 fi 145 146 if [ "$confirmation" != "Yes" ]; then 147 printf "\n" 148 read -r -p "Type 'Yes' to confirm deletion of ${#delete[@]} volumes: " confirmation >&2 149 if [ "$confirmation" != "Yes" ]; then 150 printf "Abort.\n" >&2 151 exit 1 152 fi 153 fi 154 155 for i in "${!delete[@]}"; do 156 fs="${filesystems[$i]}" 157 elevate="" 158 if [ $UID -ne 0 ]; then 159 elevate="sudo" 160 error=$($elevate true 2>&1) 161 if [ $? -ne 0 ]; then 162 error="${error#sudo: }" 163 printf "Error: Could not elevate privileges: %s\n" "${error^}" >&2 164 exit 1 165 fi 166 fi 167 printf "Deleting filesystem '%s'...\n" "$fs" >&2 168 error="$($elevate zfs destroy "$fs" 2>&1)" 169 if [ $? -ne 0 ]; then 170 printf "Error: Could not delete filesystem: %s\n" "${error^}" >&2 171 exit 1 172 fi 173 done 174