Un semplice sistema di backup in bash shell

Il primo ostacolo che si incontra è quando un comando come find -newer non rileva files rinominati poiché i files non vengono toccati ma viene alterata la tabella del file system che associa i nomi agli inodes.

L'unico modo per verificare se un file viene modificato è controllare tutte le sue caratteristiche ossia:

  • inode
  • size
  • nome
  • ctime
  • mtime
  • owner
  • group
  • permessi

Per fortuna il comando find mette a disposizione l'opzione printf che consente l'output di tutte queste caratteristiche, quindi ecco come funziona lo script:

  • Si effettua un find di qualunque file in tutto l'albero di una data directory ed il risultato, una full-list, viene mandato su un file
  • Se un backup precedente aveva già creato il file lista, questo e quello appena generato vengono confrontati con un banale diff che estrae le righe del nuovo modificate rispetto al vecchio, ossia l'elenco dei files cambiati o aggiunti
  • Se invece non esiste un backup precedente, la lista viene tenuta nella sua integrità
  • Il file risultante (quello differenziale nel primo caso, l'elenco completo nel secondo) viene quindi passato a tar che crea il file targzippato
  • Infine, la lista completa viene copiata sul file di riferimento per il confronto in un backup futuro.

Quale problema rimane ancora? Scoprire i files cancellati. È semplice e non è necessario farlo in fase di backup; semmai in un momento qualunque prima del restore: se ho conservato anche gli elenchi completi, basta estrarre dal loro confronto i files che prima c'erano e poi non più, dopo aver escluso i rinominati (riconoscibili dall'inode).

Attenzione che lo script non identifica files modificati che hanno mantenuto dimensione e data di modifica, per la cui cosa si può inserire un nuovo script che lo faccia sfruttando una checksum (es. MD5) ma rallenterebbe moltissimo.


Supponiamo che io abbia elencato le directory di cui voglio effettuare backup nel file /var/bklist e che la mia destinazione sia /var/backup (entrambi devono essere stati creati prima)


#!/bin/sh
TIME=$(date '+%Y%m%d%H%M%S')
DESTDIR=/var/backup
DIRLIST=/var/bklist
for LIST in $(cat $DIRLIST)
do
LSTNAME=$(echo "$LIST"|awk -F: '{print $1}')
STARTDIR=$(echo "$LIST"|awk -F: '{print $2}')
cd $STARTDIR
$(find . ! -type d -printf "%i?%s?%t?%c?%U?%G?%m?%p\n"> "$DESTDIR"/"$TIME"."$LSTNAME".full )
if [ -f $DESTDIR/$LSTNAME.full ]
then
echo DOING DIFF ON $DESTDIR FOR $LSTNAME FROM $STARTDIR
diff $DESTDIR/$LSTNAME.full $DESTDIR/"$TIME"."$LSTNAME".full |awk -F'?' '/^>/ {print $NF}' > $DESTDIR/$TIME.$LSTNAME.lst
else
echo DOING FULL ON $DESTDIR FOR $LSTNAME FROM $STARTDIR
cat $DESTDIR/"$TIME"."$LSTNAME".full|awk -F'?' '{print $NF}'> $DESTDIR/$TIME.$LSTNAME.lst
fi
tar cfz $DESTDIR/$TIME.$LSTNAME.tar.gz -T $DESTDIR/$TIME

Il file di elenco delle directory da backuppare sarà così composto:

NomeFileDiBackup:Directory

es:


usr.local:/usr/local
etc:/etc
MiaHome:/home/user