-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathvbak.sh
434 lines (403 loc) · 9.82 KB
/
vbak.sh
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
#!/bin/bash
#
# Copyright (C) 2008-3001, vvoody <wxj.g.sh{AT}gmail.com>
# Released under GNU GPL v3.
#
# vbak first finished without test strongly at 2008-10-20 night
# vbak can be for daily use, I think, at 2008-10-27 night
# vbak begins rewriting at Fri Aug 20 CST 2010
#
# Get the latest version:
# git clone git://github.com/vvoody/vbak.git
#
# TODO:
# 1. not make path of file as part of file name, but like unix tree
# 2. exclude files/dirs(.gitignore?)
# 3. sync the not excluded files, others will be controled by git
# 4. reverse syncing files(cp files backuped in storage to their origin place)
# 5. work with Dropbox
# remove the temp files periodically;
# find a easy to remove one line from file;
# die $exit_code $last_words
function die() {
result=$1
shift
printf "%s\n" "$*" >&2
exit $result
}
function usage() {
die 1 'Try to run "vbak help [-t] for help.'
}
function show_help() {
if [ -z $1 ]; then
echo "Usage: vbak [COMMAND] [OPTION] ..."
echo "Commands are:"
echo -e "\thelp\tadd\tremove\tupdate"
echo -e "\tlist\tfiles\tclear\tcheckout/co"
echo
echo 'See more info: "vbak help -t"'
elif [ $1 == "-t" ]; then
echo "We have only *one* option: '-t'"
echo "Please put the '-t' option just after the COMMAND."
echo
echo "Command HELP can be with '-t', or without."
echo "$ vbak help"
echo "$ vbak help -t"
echo
echo "Command LIST and FILES can use '-t' option with its value, or none."
echo "$ vbak list -t emacs"
echo "$ vbak files"
echo
echo "Command REMOVE, UPDATE and can have '-t' option with its value, "
echo " or alternatively have its arguments."
echo "$ vbak remove .bashrc .lftp"
echo "$ vbak update -t network"
echo
echo "Command ADD must have its arguments."
echo "You can also use '-t' option with its *only* one value."
echo "$ add .emacs .bashrc .mplayer/"
echo "$ add -t opera opera6.ini opera6.adr wand.dat usejs/"
echo
echo "Command CHECKOUT(or CO) and CLEAR can only use like:"
echo "$ vbak clear"
echo "$ vbak co -t rc.3"
echo
else
die 1 "Panic: Unrecognized option. Only you can use is '-t'."
fi
exit 0
}
# check_files $file1 $file2 ...
# Check if files exist, else create it.
function check_files() {
check=0
for file
do
[ -f "$file" ] && continue ||
touch "$file" || check=1
done
return $check
}
# Read the variable VBAK_OUT_DIR setting.
function check_config() {
out_dir=$(grep "$1" $2 | sed 's/^.*= *//')
return $out_dir
}
# Get the tag name and set TAGNAME
function get_tag_name() {
if [ "$1" == "-t" ]; then
if [ -f "$2" ]; then
die 1 "You should define the tag name after '-t'."
else
TAGNAME="$2"
export TAGNAME
fi
fi
}
# We will use the encoded file name as the backup file's name.
# e.g. /home/vvoody/dic.txt --> home-vvoody-dic.txt
function encode_file_name() {
#echo $1 >&2
tmp0=`echo $1 | sed -e 's/\-/\\\-/g'`
#echo $tmp0 >&2
tmp1=`echo $tmp0 | sed -e 's#/#\-#g'`
#echo $tmp1 >&2
tmp2=`echo $tmp1 | sed -e 's/^\-//g'`
echo $tmp2
return 0
}
# .emacs -> /home/vvoody/.emacs
# Called by do_add and case
function full_file_name() {
f_name=""
foo=$(echo "$1" | sed -e 's/\/$//g')
# no '/' tail
if echo "$foo" | egrep "^/" >/dev/null ; then
f_name="$foo"
else
f_name="$PWD/${foo}"
fi
echo "$f_name"
}
# remove_line which_line
# called by do_remove and do_add
# This is too bad and ugly, I wanna use sed!
function remove_line() {
tmp=$(mktemp /tmp/vbak.$RANDOM.XXXXXXX) || return 2
egrep -v "$1" "$list_file" > $tmp
cat $tmp > "$list_file"
}
# Backup the files/dirs, and add them to the file.list
function do_add() {
full_name=$(full_file_name "$1")
# Maybe you are adding a file which already backuped.
egrep "$full_name" "$list_file"
if [ $? -eq 0 ]; then
echo -n "You have already backuped this file, backup again?(y/n)"
read y_or_n
if [ $y_or_n = "y" ]; then
remove_line "$full_name";
else
return 0
fi
fi
# backup( copy directly to $VBAK_OUT_DIR), add file information to file.list
if [ ! -z "$full_name" ]; then
echo "$full_name"
name=$(encode_file_name "$full_name") || return 1
echo $name
cp -rf "$full_name" "$VBAK_OUT_DIR/$name" || return 2
echo "$full_name" "@${TAGNAME}@" >> "$list_file" || return 3
else
die 4 "Backup failed!"
fi
return 0
}
# backuped files in the VBAK_OUT_DIR
# do_list [-t TAG]
function do_list() {
if [ -z "$TAGNAME" ]; then
tmp="$list_file"
sed -i 's/@.*@//g' "$tmp"
# ls -1 -t $VBAK_OUT_DIR | egrep -v "file.list"
else
tmp=$(mktemp /tmp/vbak.$RANDOM.XXXXXXX) || return 2
egrep "$TAGNAME" "$list_file" | sed 's/@.*@//g' > $tmp
fi
while read line
do
file_bak_name=$(encode_file_name "$line") || return 3
if [ -e "${VBAK_OUT_DIR}/${file_bak_name}" ]; then
echo "$file_bak_name"
else
echo -n "$file_bak_name "
echo -e ${red}"*MISSED!*"${clr_normal}
fi
done < "$tmp"
return $?
}
# The genuine files
# do_files [-t TAG]
function do_files() {
if [ -z "$TAGNAME" ]; then
cat "$list_file" | sed 's/@.*@//g' || return 1
else
cat "$list_file" | egrep "@${TAGNAME}@" | sed 's/@.*@//g' || return 2
fi
return 0
}
# do_remove $file
function do_remove() {
# remove file record from file.list
# I need sed...
egrep "$1" "$list_file" > /dev/null
if [ $? -eq 0 ]; then
# remove backuped file from $VBAK_OUT_DIR
file_bak_name=$(encode_file_name "$1")
rm -rf "${VBAK_OUT_DIR}/${file_bak_name}"
remove_line "$1";
else
die 1 "$1 not backuped!"
fi
}
# do_update_dir $my_full_dir_path $orgin_full_bak_path
do_update_dir() {
# echo "/xx/$(basename "$2")"
for f in "$1"/*; do
f_img="${2}/$(basename "$f")"
if [ "$f" -nt "$f_img" ]; then
if [ -d "$f" ]; then
rm -rf "$f_img"
fi
cp -rf "$f" "$f_img"
echo -n "$f "
echo -e ${blue}"*UPDATED!*"${clr_normal}
else
if [ -d "$f" ]; then
do_update_dir "$f" "$f_img";
fi
fi
done
}
# do_update $file
function do_update() {
# Detect file disappearing
if [ ! -r "$1" ]; then
echo -e "$1 "${red}" *Disappeared!*"${clr_normal}
return 1
fi
# If the file is newer than the backuped file,
# then we will update it.
egrep "$1" "$list_file" > /dev/null
if [ $? -eq 0 ]; then
file_bak_name="$(encode_file_name "$1")"
if [ "$1" -nt "${VBAK_OUT_DIR}/${file_bak_name}" ]; then
if [ -d "$1" ]; then
rm -rf "${VBAK_OUT_DIR}/${file_bak_name}"
fi
cp -rf "$1" "${VBAK_OUT_DIR}/${file_bak_name}"
echo -n "${1} "
echo -e ${blue}"*UPDATED!*"${clr_normal}
else
if [ -d "$1" ]; then
do_update_dir "$1" "${VBAK_OUT_DIR}/${file_bak_name}";
fi
fi
else
die 1 "${1} not backuped!"
fi
}
# checkout a group of files with TAGNAME
function do_checkout() {
egrep "@${TAGNAME}@" "$list_file" > /dev/null
if [ $? -eq 0 ]; then
tmp=$(mktemp /tmp/vbak.$RANDOM.XXXXXXX) || return 2
egrep "@${TAGNAME}@" "$list_file" | sed 's/@.*@//g' > $tmp
local let counter=0
while read line
do
file_bak_name=$(encode_file_name "$line")
file_base_name=$(basename "$line")
if [ -e "${VBAK_OUT_DIR}/${file_bak_name}" ]; then
let ++counter
cp -rf "${VBAK_OUT_DIR}/${file_bak_name}" "./$file_base_name"
else
echo -n "${file_bak_name} "
echo -e ${red}"*missed!*"${clr_normal}
fi
done < $tmp
else
die 1 "No files checkouted!"
fi
echo "${counter} file(s) checkouted!"
}
# Empty the VBAK_OUT_DIR
# do_clear
function do_clear() {
echo "We will remove all backuped files and clear up the file.list"
echo -n "Are you sure? "
read y_or_n
if [ $y_or_n == "y" ]; then
rm -rf $VBAK_OUT_DIR/*
else
return 1
fi
return 0
}
##### MAIN #####
if [ $# = 0 ]; then
usage
fi
rcfile="$HOME/.vbakrc"
export rcfile
default_out_dir="$HOME/.vbak"
check_files $rcfile || die $? "Panic: I don't hope we will reach here."
VBAK_OUT_DIR=$(grep "VBAK_OUT_DIR" $rcfile | sed 's/^.*=//')
if [ -z "$VBAK_OUT_DIR" ]; then
VBAK_OUT_DIR=$default_out_dir
fi
if ! echo $VBAK_OUT_DIR | egrep "^\/"; then
VBAK_OUT_DIR="$HOME/$VBAK_OUT_DIR"
fi
export VBAK_OUT_DIR
if [ ! -d "$VBAK_OUT_DIR" ]; then
mkdir -p "$VBAK_OUT_DIR"
fi
list_file="$VBAK_OUT_DIR/file.list"
check_files "$list_file"
export list_file
# Make error info more clearly.
red='\e[0;31;1m'
blue='\e[0;34;1m'
green='\e[0;32;1m'
clr_normal='\e[0m'
case "$1" in
help)
help_opt=$2
show_help $2;
;;
list)
shift
get_tag_name "$@"
do_list;
;;
files)
shift
get_tag_name "$@"
do_files;
;;
add)
shift
get_tag_name "$@"
# obmit the option and tag name
if [ ! -z "$TAGNAME" ]; then
shift
shift
fi
for file in "$@"
do
echo -n "Add "
echo -e ${green}"$file"${clr_normal}
do_add "$file";
done
;;
remove)
shift
get_tag_name "$@"
if [ ! -z "$TAGNAME" ]; then
tmp=$(mktemp "/tmp/vbak.$RANDOM.XXXXXXX") || return 2
egrep "@${TAGNAME}@" "$list_file" | sed 's/@.*@//g' > $tmp
while read line
do
do_remove "$line";
done < $tmp
else
for f in "$@"
do
# full the file name
full_name=$(full_file_name "$f")
do_remove "$full_name";
done
fi
;;
update)
shift
get_tag_name "$@"
# vbak update OR vbak update -t TAGNAME
if [[ ( -z "$1") || (! -z "$TAGNAME") ]]; then
tmp=$(mktemp "/tmp/vbak.$RANDOM.XXXXXXX") || return 2
if [ -z "$TAGNAME" ]; then
sed 's/@.*@//g' "$list_file" > $tmp
else
egrep "@${TAGNAME}@" "$list_file" | sed 's/@.*@//g' > $tmp
fi
while read line
do
do_update "$line";
done < $tmp
else
for f in "$@"
do
# full the file name
full_name=$(full_file_name "$f")
do_update "$full_name";
done
fi
;;
check|co)
shift
get_tag_name "$@"
if [ -z "$TAGNAME" ]; then
die 1 "Don't leave checkout alone, give him '-t' and a tag name."
fi
do_checkout;
;;
clear)
do_clear;
;;
*)
echo "Unknown command!" >&2
usage;;
esac
# do some cleanup
rm -f /tmp/vbak.* > /dev/null