#!/bin/bash ## restore from backup made by bk; by Eugene Reimer 2010-12-01; ## PREREQ: bk fullnameNOSLASH -- from http://ereimer.net/programs/ertools.zip ## ## USAGE: ## bk-restore [--maxyears=MAXYEARS] FILENAME... ## restores the most recent versions to /tmp/bk-restore-FILENAME-YYYYMMDD; for backups from the last MAXYEARS years, default:1; shopt -s extglob ##enable extglob for extended-patterns (negation etc); is needed in bk, not needed here?? PROJLIST="beans books debwendon er ereimer etc flora noci" ##==the "projects" getting regular bk-backups; derived from bkall+bk BKDIR=/pix/bkup ##==where to find gzipped bk-backups; derived from bk LOGFILE=/tmp/bk-restore-LOG-$(date +%Y%m%d-%H%M) ##==where to log MAXYEARS=1 ##default for optional param ##=====example of new improved arg-parsing style: USAGE="bk-restore [--maxyears=MAXYEARS] FILENAME..." getopnd () { if [[ $ARG == *=* ]];then OPX="${ARG#*=}";else ((++J)); OPX="$NXT"; fi; } ##Note: implicit param J is updated in next-word case; result $OPX N=$# ##set N = nbr-args; could use $# in for-expr but need to evaluate just once?? for((J=1;J<=N;++J));do ARG=${!J}; ((X=J+1));NXT=${!X} ##for J from 1..N do, setting ARG to the Jth arg... if [[ $ARG != -* ]];then break; fi ##leave on encountering non-option if [[ $ARG == -- ]];then shift; break; fi ##leave on encountering "--" allowing subsequent non-option to start with dash case "$ARG" in -h*|--help) echo "$USAGE"; exit 0 ;; -m*|--max*) getopnd;MAXYEARS=$OPX ;; ##getopnd procedure-call to handle same- or next-word opnd *) echo "unrecognized option: $ARG"; echo "$USAGE"; exit 1 ;; ##errmsg for unknown option esac done for((J--;J--;));do shift; done ##remove args 1..J-1 (an extra shift has been in the "--" case) if [ $# -eq 0 ];then echo "FILENAME required";echo "$USAGE"; exit 1;fi ##errmsg if no FILENAME on cmdline ##=====endof arg-parsing example for f;do ##for each NonOption arg f... F=$(fullname $f) ##get F fully-qualified filename PROJ=${F:1}; PROJ=${PROJ%%/*}; FB=${F#/*/} ##PROJ gets first pathname-segment of F; FB gets all but first pathname-segment if [[ $PROJLIST != *$PROJ* ]];then echo "no bk backups for $PROJ"; exit 9; fi ##errmsg for invalid PROJ [[ $FPAT == "" ]] || FPAT="$FPAT|"; FPAT="$FPAT$FB" ##form FPAT an egrep-pattern for filenames [[ $PPAT == "" ]] || PPAT="$PPAT|"; PPAT="$PPAT$PROJ" ##form PPAT an egrep-pattern for proj, will be converted to a Glob done; [[ $PPAT == *\|* ]] && PPAT="@($PPAT)" ##convert PPAT to Glob echo "bk-restore: PPAT:$PPAT; FPAT:$FPAT; MAXYEARS:$MAXYEARS" ##DEBUG cd /tmp || exit ##==work in /tmp for((Y=$(date +%Y),BEGY=Y-MAXYEARS; Y>BEGY; --Y));do ##for each year Y from current going back MAXYEARS years... L=$(bk list $PPAT-$Y |grep "/\(${FPAT//|/\\|}\) ") ##make list L of selected-backup-copies within year Y; Note: this is the slow part!! if [[ $L == "" ]];then continue; fi ##skip if no backup-copies this year echo "$L" |while read B junk junk2 Z;do ##process L line-by-line... line of L: etc/sbin/weblinkcheck is in etc-20101001-0622-F.tgz D=${Z#*-}; D=${D%%-*} ##get date D from name of gzip-file Z tar xzfO $BKDIR/$Z $B >/tmp/bk-restore-$(fullnameNOSLASH /$B)-$D ##extract file B from gzipfile Z, to /tmp/bk-restore-FILENAME-YYYYMMDD echo "bk-restore ->/tmp/bk-restore-$(fullnameNOSLASH /$B)-$D" ##progress-msg done echo "$L" >>$LOGFILE ##catenate L onto the log-file for this call done exit ====== NOTES: ====== SHOWING CHOICES: bk list etc-2010 |grep 'weblinkcheck.cron' <--to see DATE choices (within 2010) for backups of weblinkcheck.cron; TIME: 28sec; PERFORMANCE: consider: speeding up by keeping a TOC-file for each zipfile; space requirement: /pix/bkup/etc-20101027-0610-I.tgz is 161KB and is typical daily incremental backup; its TOC-file at 1.0KB suggests approx 1% space-overhead; eg2: /pix/bkup/etc-20100701-0622-F.tgz is a full backup; size: 10,600KB; its TOC-file at 274KB suggests 2.5% space-overhead for a full; the /etc directory probably has the smallest average file-size of all my backed-up dirs => its overhead-ratio expected to be the worst-case; HOW DATES WORK in backup-copies: bk-auto.cron is run at 06:10am, so zipfile with YYYYMMDD in its name contains version from the-day-before that; in fact it's surprisingly close to being in accordance with my ERW-dates (where days change at 8am), except ONE-DAY out; RATIONALE: if showing choices were quick then I'd be happy with tools to: (1) show choices (bk list), (2) restore-by-date OR dif-by-date; however with present data organization showing the choices is by far the slowest part, ergo, it becomes important to do the slow part just once, which means making named-by-date restored files at the same time as showing the choices!! which leads to the way this command works; furthermore, the way this command works means there's no need for a dif-by-date command; PERFORMANCE-2: year-by-year loop seems silly; typically there are more backup zipfiles in current year than in all preceding years together; ==ergo a 2-way retrieval-choice would be more sensible: either restrict to current year OR do ALL years?? also, in January need more than current-year?? doing multiple files at once; same price (in time) when they're from same PROJ; bk-list already supports a Glob as the Nm arg, ie: can use @(nm|nm); needs: shopt -s extglob!! PROJLIST: bkall: for NM in beans books debwendon erall ereimer etc flora nociall;do bk $VRB $NM; done bk: erall: NM=er; tar...|gzip >$TGTDIR/$NM-$TIME-$TY.tgz bk: nociall: NM=noci; tar...|gzip >$TGTDIR/$NM-$TIME-$TY.tgz PROJLIST: beans books debwendon er ereimer etc flora noci =========== CHANGE-LOG: =========== fixed: orig pattern-matching misbehaved when restoring something like /er/website/20100725/index.htm -- because there are many /er/website/*/index.htm files; DONE; fixed: runs w/o any errmsgs but the tar target files are all empty; works with separate gzip+tar commands; also works if no leading dash on tar options!! that line was: tar -xzfO $BKDIR/$Z $B >/tmp/bk-restore-$(fullnameNOSLASH /$B)-$D this works: tar xzfO $BKDIR/$Z $B >/tmp/bk-restore-$(fullnameNOSLASH /$B)-$D this works: gunzip -c $BKDIR/$Z |tar xvfO - $B >/tmp/bk-restore-$(fullnameNOSLASH /$B)-$D fixed: egrep treats "+" or "(" as metachar => FPAT needs + to \+ revision; OR: simpler to use grep with | to \| revision; ==FIXME: when naming the option --maxyears, I was thinking of having some criteria for "enough" backup copies that could terminate the loop before doing all years up to that "max" number into the past, however such criteria hasn't been defined nor does it seem needed, ergo the "max" in the name seems misleading?? timestamps on restored files restored; could avoid extracting to stdout but want the renaming; crude: use touch to restore date as date-of-backup?? touch -d$D /tmp/bk-restore-$(fullnameNOSLASH /$B) ##set file's date based on date of backup; crude and worse than useless?? 2011-01-05: (finally got back to this unfinished script, after lengthy digressions on Photo-Uploader, Christmas-card, Insect-Photos-webpage) added test for $L being empty, when there are no backups in year $Y; observed that processing year 2011 is blessedly quick in early-January:-) ie: maxyears=2 in early-Jan has much the running-time as maxyears=1 in late-Dec; multiple files not working, because extglob shell-option is not inherited by subshell; added set extglob in bk; multiple files now work!! added it to webmaster-tools page; and in programs/README.htm, section on CMDLINE-OPTIONS: now mention bk-restore as my solution to bash-excercise I pose; 2011-01-06: bk-auto.cron: fixed start-of-QTR test; made FULL-bkups for today; enabled pruning of backups; reran for 20101001 20110101 to catch-up on pruning;