Появилось немного времени и я решил переписать скрипт для автоматизации задачи бэкапа данных. Раньше скрипт неизбежно приходилось дублировать, если каталоги назначения различались, для локального же бэкапа приходилось немного модифицировать сценарий. Теперь этого делать не нужно, один универсальный скрипт способен проделать несложные операции за вас, нужно лишь задать необходимые параметры в файле настроек.

Итак, ZLU (ZeroLab Unisync):
#!/bin/sh

# --------------------------------------------
# ZLU (ZeroLab Unisync):
# remote & local automated backup script
# Written by ZeroChaos
# Version 1.5, 2012-11-20
# Site: https://zerolab.net/
# --------------------------------------------
# NOTE: Exclude spaces in the variable FOLDER!
# --------------------------------------------

# -------------------------------------------- begin of [Configurable settings]
# Use SSH for remote sync
export RSYNC_RSH="ssh -c arcfour -o Compression=no -x"

# Snapshot directory
SDIR="snapshot"
SNAPSHOT_dir="$SDIR/`date +'%Y-%m-%d.%Hh%Mm'`"

# Take the list from the same folder where the script is called
DIR=$(dirname "$0")
LISTFOLDERS="$DIR/backup.txt"
EXCLUSIONSDIR="$DIR"
# -------------------------------------------- end of [Configurable settings]

# -------------------------------------------- begin of [Do not touch this section]
# If no arguments
if [ $# = 0 ]; then
SNAPSHOT_GLOBAL="disable"
i=0
while read LINE; do
if [ -n "$LINE" ]; then
IFS=";"
set -- $LINE
FOLDER="$1"
DEST_point="$2"
LISTEXCLUSIONS="$EXCLUSIONSDIR/$3"
SNAPSHOT="$4"
SDAYS="$5"
REMOTEHOST="${6}@${7}"
FOLDERNAME="`echo "$FOLDER" | awk -F/ '{ print $NF }'`"
# Options for rsync + array of commands to autorotation
if [ $SNAPSHOT = "enable" ]; then
SNAPSHOT_GLOBAL="enable"
if [ "$6" != "" ] && [ "$7" != "" ]; then
OPTIONS="-Sazq --bwlimit=3840 --delete --exclude-from=$LISTEXCLUSIONS --backup --backup-dir=${DEST_point}${SNAPSHOT_dir}/$FOLDERNAME"
SAR[$i]="ssh $REMOTEHOST \"find ${DEST_point}${SDIR}/ -type d -mtime +$SDAYS -exec rm -r {} \;\" >/dev/null 2>&1"
i=$(($i+1))
else
OPTIONS="-Saq --delete --exclude-from=$LISTEXCLUSIONS --backup --backup-dir=${DEST_point}${SNAPSHOT_dir}/$FOLDERNAME"
SAR[$i]="find \"${DEST_point}${SDIR}/\" -type d -mtime +$SDAYS -exec rm -r {} \; >/dev/null 2>&1"
i=$(($i+1))
fi
else
if [ "$6" != "" ] && [ "$7" != "" ]; then
OPTIONS="-Sazq --bwlimit=3840 --delete --exclude-from=$LISTEXCLUSIONS"
else
OPTIONS="-Saq --delete --exclude-from=$LISTEXCLUSIONS"
fi
fi
# Sync
if [ "$6" != "" ] && [ "$7" != "" ]; then
eval rsync $OPTIONS "${FOLDER}/" $REMOTEHOST:"${DEST_point}${FOLDERNAME}/"
else
eval rsync $OPTIONS "${FOLDER}/" "${DEST_point}${FOLDERNAME}/"
fi
fi
done < $LISTFOLDERS # Snapshot auto-rotate if [ $SNAPSHOT_GLOBAL = "enable" ]; then for ((a=0; a<${#SAR[*]}; a++)); do eval "${SAR[$a]}" done fi fi # Generate public/private rsa key pair ssh_keygen() { ssh-keygen -t rsa -b 4096 } # Copy your public key on a remote machine ssh_copy_id() { # Function for remote host choice PS3='Select the remote host: ' rhchoice() { eval set $RHARR select RH; { REMOTEHOST=$RH break } } # Read the list of remote hosts while read LINE; do IFS=";" set -- $LINE if [ "$6" != "" ] && [ "$7" != "" ]; then RH="${6}@${7}" RHARR="$RHARR $RH" fi done < $LISTFOLDERS # Remote host choice if [ "$RHARR" != "" ]; then rhchoice $RHARR else echo "No remote hosts, please configure the settings file: $LISTFOLDERS" fi ID_FILE="${HOME}/.ssh/id_rsa.pub" if [ x$SSH_AUTH_SOCK != x ] && ssh-add -L >/dev/null 2>&1; then
GET_ID="$GET_ID ssh-add -L"
fi

if [ -z "`eval $GET_ID`" ] && [ -r "${ID_FILE}" ]; then
GET_ID="cat ${ID_FILE}"
fi

if [ -z "`eval $GET_ID`" ]; then
echo "$0: ERROR: No identities found" >&2
exit 1
fi

if [ "$RHARR" != "" ]; then
{ eval "$GET_ID" ; } | ssh ${REMOTEHOST%:} "umask 077; test -d .ssh || mkdir .ssh ; cat >> .ssh/authorized_keys" || exit 1
fi
}

ME=$(basename "$0")
print_help() {
echo
echo "Usage: $ME [OPTION...]"
echo
echo "Options:"
echo " -g Generate public/private rsa key pair:"
echo " ssh-keygen -t rsa -b 4096"
echo " -c Copy your public key on a remote machine:"
echo " ssh-copy-id user@host"
echo " -h Help."
echo
echo "Example: $ME -gc"
echo "Use both options, generate & copy."
echo
}

while getopts ":gch" opt;
do
case $opt in
g) ssh_keygen;
;;
c) ssh_copy_id;
;;
h) print_help;
;;
*) echo "Wrong option";
echo "For help, use: $ME -h";
exit 1
;;
esac
done
# -------------------------------------------- end of [Do not touch this section]

Файл настроек принял следующий вид:
backup.txt:
/home/zerochaos/www/zerolab.net;/home/zerochaos/www/;exclude.txt;enable;15;zerochaos;backup.zerolab.net
/Users/zerochaos/data;/Volumes/BACKUP/;exclude.txt;disable;30
# [source_folder][destination_folder][exclusions_list][snapshot_trigger][snapshot_days][user][host] -- [do not delete this string]

Как видите, в одном файле настроек можно задать параметры как для удаленной, так и для локальной синхронизации. Разберем параметры каждого столбца, что за что отвечает:
1) [source_folder]: каталог, резервную копию которого мы делаем
2) [destination_folder]: каталог, куда мы сохраняем резервную копию
3) [exclusions_list]: список с масками исключений (формат смотрите в мане rsync)
4) [snapshot_trigger]: включаем или выключаем создание снапшотов
5) [snapshot_days]: указываем количество дней для авторотации снапшотов
6) [user]: имя пользователя удаленного хоста, на который производится резевное копирование
7) [host]: имя или IP этого хоста
Разделитель: «;»

6 и 7 параметры следует указывать только в том случае, если мы производим резервное копирование на удаленную машину. Заполняем список заданий для удаленного и локального бэкапа в одном файле, скрипт выполнит цикл резервного копирования. Каждое задание на новой строке, последнюю строку с комментариями не удаляем.

Пример exclude.txt:
temp/summ/checks/*
temp/sess/*
temp/tmp/*
template_cache/*
cache/*
logs/*

Напомню про snapshot’ы. Скрипт позволяет опционально делать snapshot’ы, содержащие измененные, либо удаленные файлы. Тем самым, появляется возможность откатиться на старые версии файлов, случайно удаленных, к примеру, либо ошибочно измененных. При этом сохраняется полная изначальная структура каталогов, что позволяет легко найти требуемый файл. Snapshot’ы хранятся в каталогах со следующей маской в имени: ‘год-месяц-день.XXчXXм’. Есть возможность авторотации snapshot’ов, выставляете количество дней, отвечающее вашим требованиям, и имеете возможность спокойно восстановить рассортированные данные за эти дни.

Как и ранее, имеется возможность генерации ключа для авторизации по SSH, а так же его копирования на backup-сервер. Для авторизации с backup-сервером воспользуемся беспарольным вариантом, т.е. авторизацией по ключу. Для этого сгенерируем пару public/private rsa key. Выполним скрипт с ключом -g:

./backup.sh -g

Отвечаем на заданные вопросы и переходим к копированию открытого ключа на удаленный сервер. Для этого выполним скрипт с ключом -c:

./backup.sh -c

Будет выведен список удаленных хостов. Выбираем.

Вводим пароль пользователя, под которым будет происходить дальнейшая авторизация по ключу во время резервного копирования. Две команды выше можно объединить в одну, тогда операции будут выполнены последовательно:

./backup.sh -gc

Остается добавить скрипт в cron:

#minute hour mday month wday command
0 1 * * * bash /scripts/backup.sh

Скрипт будет работать в любой операционной системе, где возможна установка rsync client и имеется командный интерпретатор.