Создание снимков каталогов для бекапа

Сделать бекап очень просто: достаточно выполнить команду:

$ find . -newer $last_backup -type f -exec cp {} $backup_dir \;

Эта команда скопирует все файлы которые изменились с момента создания прошлого бекапа ($last_backup) в каталог $backup_dir. Конечно нужно найти последний бекап: это можно сделать командой ls, к ключом -t прогнав её вывод сквозь фильтр head -n1.

Подсказка

Что-бы новые файлы не затирали копии старых, у cp есть опция -V METHOD, --version-control=METHOD, см. man cp.

Всё это прекрасно, но мне надо сохранять только разные файлы, несмотря на то, что нужно так-же сохранять информацию о тех файлах, время модификации которых позже последнего бекапа, однако их содержимое не изменилось.

Потому мне придётся создавать списки новых файлов (они так-же будут служить временными метками для поиска новых файлов), и кроме того нужно хранить контрольную сумму. Её я буду хранить прямо в имени файла, через точку.

Т.о. мы приходим к сл. алгоритму:

  1. Прежде всего мы получаем имя списка последнего бекапа командой
    LAST_LST=`/bin/ls -t *.lst | sed 's/^/-newer /; q'`

    Замечание

    Если такого списка ещё нет, то команда завершится с ошибкой, что впрочем нас вполне устраивает
    Команда sed не только фильтрует нужный файл, но ещё и добавляет к нему ключ -newer.
  2. Получив имя самого последнего списка мы ищем новые и изменённые файлы
    find $BACKUP_DIRS $LAST_LST -type f -iname "*.*"
  3. Далее, полученный список мы отдаём sed-скрипту, который
    1. Проверяет корректность имени файла
    2. Копирует файл
    3. Вычисляет контрольную сумму
    4. Проверяет, есть-ли файл с таким именем и содержимым.
    5. Если файл есть, то копия удаляется, а в список заносится имя файла перед которым стоит `~'.
    6. Если такого файла нет, то он сохраняется, и записывается в список с символом `+'.

Вот скрипт на bash'е

Пример 4.18.

#!/bin/bash

SNAPSHOTS="/tmp/bsnapshots"
cd "$SNAPSHOTS" || exit

BACKUP_DIRS="/home/doc/docs/sed"
EXE_DIR="/home/doc/docs"

CUR_DATE=`date +"%y%m%d%H%M"`

LAST_LST=`/bin/ls -t *.lst | sed 's/^/-newer /; q'`


find $BACKUP_DIRS $LAST_LST -type f -iname "*.*" |\
$EXE_DIR/bsnapshot.sed > "$CUR_DATE.lst"
err=$?
if [ $err != "0" ]; then
	echo "ERROR $err"
	exit $err
fi

if [ -s "$CUR_DATE.lst" ]; then
	cat $CUR_DATE.lst
else
	rm $CUR_DATE.lst
fi


И этот скрипт вызывает sed-скрипт:

Пример 4.19.

#!/bin/sed -rnf

# файлы для бекапа должны быть с полным путём
\|^/[^/"]+/[^"]*[^/"]$|! q 79

h
# копирование файла во временный каталог
s/.*/cp --parents "&" "."; echo $?/e
/^0$/!  q 77
g
# вычисление контрольной суммы
s|.*|cksum ".&"|e
# получение нового имени файла
s|^([0-9]+)\s+[0-9]+\s+(.*)|\2.\1|
# проверка
h
# ./home/doc/...
s|.*|test -f "&"; echo $?|e
/^0$/ {
	# этот файл уже есть в бекапах
	g
	s/.*/~&/p
	# удаление копии
	s/^~(.*)\.[0-9]+$/rm \1/e
	b
}

# такого файла ещё нет
g
s|^(.+)\.([0-9]+)$|mv "\1" "\1.\2"; echo $?|e
/^0$/! q 78
g
s/^/+/p


Я не сохраняю пустые каталоги, однако каталоги с файлами создаются. Этот скрипт так-же проверяет коды завершения команд которые он вызывает. В случае ошибок работа скрипта прерывается. Время от времени нужно сжимать этот временный каталог.

Замечание

К примеру этот учебник написан в формате docbooks, и после его сборки получается множество HTML файлов (которые вы и читаете), проблема в том, что эти файлы почти все не изменяются (не могу-же я изменить содержимое всех глав сразу!). Однако время изменения совпадает со временем последней сборки. Та-же самая ситуация возникает, если мы решим забекапить большую базу данных - мало вероятно, что сразу изменятся все базы и все таблицы.

Вы можете обсудить этот документ на форуме. Текст предоставляется по лицензии GNU Free Documentation License (Перевод лицензии GFDL).

Вы можете пожертвовать небольшую сумму яндекс-денег на счёт 41001666004238 для оплаты хостинга, интернета, и прочего. Это конечно добровольно, однако это намного улучшит данный документ (у меня будет больше времени для его улучшения). На самом деле, проект часто находится на грани закрытия, ибо никаких денег никогда не приносил, и приносить не будет. Вы можете мне помочь. Спасибо.