-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathzdiffwalk
executable file
·158 lines (137 loc) · 3.58 KB
/
zdiffwalk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#!/usr/bin/env bash
zdiffwalk_usage() {
cat <<EOusage | mdformat
**NAME**
${0##*/} - walk through zfs snapshots, showing diffs
**SYNOPSIS**
${0##*/} [**-t**] _dataset_
**DESCRIPTION**
Walks through a dataset's zfs snapshots, listing the file
differences between each pair
Options:
**-t** include timestamp of changed files
Once running, you can nagivate using:
**j** next pair
**k** previous pair
**l** display a list of snapshots
**g** go to a specific point in the list
**z** zoom
**r** refresh snapshot list
**q** quit
EOusage
}
zdiffwalk_briefusage() {
cat <<EObrief | mdformat
**j** = next, **k** = previous, **l** = list snapshots, **g** = go snap,
**z** = zoom, **r** = refresh list, **q** = quit
EObrief
}
mdformat() {
local md so us mr me
if [ -t 1 ]; then
md=$(tput md) # bold
so=$(tput so) # standout (inverse?)
us=$(tput us) # underline
mr=$(tput mr) # reverse
me=$(tput me) # exit attributes
fi
sed -E \
-e "s/\*\*([^*]+)\*\*/$md\1$me/g" \
-e "s/\*([^*]+)\*/$us\1$me/g" \
-e "s/_([^_]+)_/$us\1$me/g" \
-e "s/^#+ *(.+)/$so\1$me/g" \
-e "s/#([^#]+)#/$mr\1$me/g"
}
zdiffwalk_vars() {
local -g zdiffwalk_snaps=()
local -g Verbose
# Defaults
if [ -t 0 ]; then
Verbose=true
else
Verbose=false
fi
showtime=false
}
zdiffwalk() {
zdiffwalk_vars
local i opt
local lhs=0
# Options
while getopts vqt opt; do
case "$opt" in
v) Verbose=true ;;
q) Verbose=false ;;
t) showtime=true ;;
*) zdiffwalk_usage; exit 1 ;;
esac
done
shift $((OPTIND - 1))
[[ $# -eq 0 ]] && { zdiffwalk_usage; exit 65; }
if ! zfs list -Ho name "$1" >/dev/null; then
exit 65
elif ! zfs list -t snapshot -rHo name | grep -q "^$1@"; then
echo "cannot diff '$1': dataset has no snapshots" >&2
exit 65
fi
mapfile -t zdiffwalk_snaps < <(zfs list -t snapshot -rHo name -d 1 "$1")
if [ ${#zdiffwalk_snaps[@]} -eq 1 ]; then
if $showtime; then
zfs diff -t "${zdiffwalk_snaps[$lhs]}" | sed 's/^\([0-9]*\).[0-9]*/\1/' |
while read -r d t line; do printf "%s\t%s\t%s\n" "$(date -r "$d" '+[%Y-%m-%d %T]')" "$t" "$line"; done
else
zfs diff "${zdiffwalk_snaps[0]}"
fi
exit $?
fi
$Verbose && echo "Browsing ${#zdiffwalk_snaps[@]} snapshots..."
for ((i=0;i<${#zdiffwalk_snaps[@]};i++)); do
printf "(%d) %s\n" "$i" "${zdiffwalk_snaps[$i]}"
done
while :; do
echo ">> ($lhs) zfs diff \"${zdiffwalk_snaps[$lhs]}\" \"${zdiffwalk_snaps[$((lhs+1))]}\""
if $showtime; then
zfs diff -t "${zdiffwalk_snaps[$lhs]}" "${zdiffwalk_snaps[$((lhs+1))]}" | sed 's/^\([0-9]*\).[0-9]*/\1/' |
while read -r d t line; do printf "%s\t%s\t%s\n" "$(date -r "$d" '+[%Y-%m-%d %T]')" "$t" "$line"; done
else
zfs diff "${zdiffwalk_snaps[$lhs]}" "${zdiffwalk_snaps[$((lhs+1))]}"
fi
choice=""
while :; do
read -s -n 1 -p "(j/k/g/l/z/r/?/q)? " choice
echo "$choice"
case "$choice" in
q) break 2 ;;
j) ((lhs++)) ;;
k) ((lhs--)) ;;
z)
zfs diff "${zdiffwalk_snaps[$lhs]}" "${zdiffwalk_snaps[$((lhs+1))]}" | nl
;;
g)
read -p "Jump to (0-$((${#zdiffwalk_snaps[@]} - 1))): " go
if [[ "$go" =~ ^[0-9]+$ ]] && [ "$go" -lt ${#zdiffwalk_snaps[@]} ]; then
lhs=$go
fi
;;
l)
for ((i=0;i<${#zdiffwalk_snaps[@]};i++)); do
printf "(%d) %s\n" "$i" "${zdiffwalk_snaps[$i]}"
done
;&
r) mapfile -t zdiffwalk_snaps < <(zfs list -t snapshot -rHo name -d 1 "$1") ;;
\?) zdiffwalk_briefusage ;;
*) continue ;;
esac
break
done
case "$lhs" in
-1) lhs=0 ;;
${#zdiffwalk_snaps[@]}) break ;;
esac
done
}
# only execute if we're called directly
case "$-" in
*i*) : ;;
*) zdiffwalk "$@" ;;
esac