- АХТУНГ, ВАХТУНГ, БАХТУНГ, ДРЯХТУНГ, АЛЯРМ, ВНИМАНИЕ
- ЭТОТ СКРИПТ ПРЕДОСТАВЛЯЕТСЯ КАК ЕСТЬ, Я НЕ НЕСУ НИКАКОЙ
- ОТВЕТСТВЕННОСТИ ЯВНОЙ ИЛИ КОСВЕННОЙ, ВАМ ЛИБО КОМУ ТО ЕЩЁ
- ПО ПРИЧИНЕ, ПОРЧИ И/ИЛИ УНИЧТОЖЕНИИ ВАШИХ ДАННЫХ
- ЕСЛИ ВЫ НЕСОГЛАСНЫ, Я ЗАПРЕЩАЮ ВАМ ЗАПУСКАТЬ ЭТОТ КОД
- ДЕЙСТВУЙТЕ НА СВОЙ СТРАХ И РИСК, ВАШИ ЛЮБЫЕ СОМНЕНИЯ
- КАСАТЕЛЬНО СКРИПТА ДОЛЖНЫ ВЕСТИ К ОТКАЗУ ОТ ЕГО ИСПОЛЬЗОВАНИЯ
Всё, теперь если у вас что-то пойдёт не так, то я чист.
И меня никто не будет ругать, гы :) Просто будте внимательный с тем,
какой каталог указывается для сохранения бекапов. А то ведь так и затереть
чего важного можно случайно.
Вопщем решил поделится прост. Пусть будет.
Простой скрипт ручного бекапа наиболее важных файлов, использует
rsync
и zip
Настройка производится внутри стрипта. Описание
настройки бекапа тоже распологается внутри скрипта.
Типичное использование это ручной запуск в конце дня или по
завершению какой либо важной работы или перед сном :)
Используйте для бекапа, отдельный диск где нет ничего кроме бекапа!
Просто иначе, в бекапе нет никакого смысла.
Лично я просто на панельку кнопочку сделал, и перед отплытием в люльку тыкаю кнопочку.
Комп я не выключаю обычно, а если выключаю до явно дожидаюсь окончания бекапа, не проверял что
будет если увести в сон во время бекапа, так что, лучше дождаться окончания.
Зависимости
mkdir zip rsync notify-send lua
Использование
Настроить скрипт, после чего просто его запускать. Так как вам удобно.
- Обязательные параметры
source
каталог или файл источникdest
каталог приёмник
- Дополнительные параметры
secure
применить пароль (требуетсяzip=true
)zip
сжать в архивmirror
сделать точную копию и обновить им текущий каталог бекапа( или архив еслиzip=true
)versions
сделать точную копию и пометить датой в отдельном каталоге (или архиве еслиzip=true
)
Пример поля конфигурации:
{secure=true, zip=true, versions=true, source='~/.ssh/', dest='ssh'};
Значит что нужно сделать рекурсивный бекап каталога с ключами ssh
и расположить
бекап в каталоге с именем ssh
. secure=true
означает что данные будут запаролены, а
zip
что будут сжаты, versions=true
означает что каждый новый бекап будет
создавать новую копию ~/.ssh
помеченную датой. Описание того какие есть ключи
и как они работают друг с другом есть внутри скрипта, вместе с примерами уже
готовой настройки.
Ключи можно опускать (не указывать) обязательными являются только source
и dest
Не указанные ключи считаются заданными как false
так что вот такая запись валидна
{source='~/Изображения/', dest='картинки'}
И означает сделать копию каталога ~/Изображения
в каталоге картинки
внутри общего
каталога бекапа backup_path=...
при этом если это второй или последующий бекап и в
каталоге ~/Изображения/
была удалена картинка, она не будет удалена в каталоге
картинки
это сделано специально, дабы максимизировать сохраняемые данные.
Если нужна точная копия с удалением того чего уже нет в каталоге источнике то нужно
дополнительно явно указать опцию mirror=true
это же правило работает
если включить флаг zip=true
будет всё тоже самое, но уже в виде архива.
На заметку
В каталоге относительно котрого исполняется скрипт будет лог бекапа, на рабочий стол будут приходить
уведомление о текущем статусе выполнения, в случае ошибки будет показaн код возврата
В лог попадает информация о том какая именно команда потерпела неудачу, пароль присутцвующий в команде
заменяется на звёздочки. В зависимости от типа настроенного бекапа(первый бекап будет всегда долгим),
а вот последующие при отсуцтвии изменений будут быстрыми исключение это mirror
и versions
оба
создают точную копию с нуля, а не пытаются обновить текущее содержимое бекапа. В случае если любая
программа вернёт код возврата отличный от 0
выполнение всего бекапа прерывается, и повторно он не
запустится пока не будет удалён lock файл, это сделано специально, чтобы выяснить причину, исправить её
и в ручном режиме удалить lock файл название котрого есть в срипте, в логах и уведомлении на рабочий стол.:
Если вы бекапите например ~./vimrc
в dest
каталог myvimrc
то он будет там лежать как есть, с точкой.
Так что так же чтобы его увидеть в каталоге бекапа нужно нажимать ctrl+h
. Ну, я так. На всякий случай.
Удалять из бекапов лишнее, и вообще контролировать влезет ли всё, это ваша забота =)
Нет опции отключения уведомлений. Предпочитаю явно знать что всё прошло гладко.
Логи бекапа тоже руками удалять.
Дадада, пароль на zip
для ssh
и gpg
ключей хахахаха, но хоть что-то.
Сам внешний накопитель держите шифрованным если туда важное сохраняется.
Сам сриптик. Взял его как есть.
#!/usr/env lua
local backup_rule =
{
--каталог куда будут размещаться все бекапы
backup_path = '/media/$USER/STORAGE_SSD/backup';
--пароль если используется secure=true, лучше использовать файл с паролем
backup_pass = nil;
--пароль для важныйх файлов, можно записать в файл дабы не светить
backup_pass_file = "/home/$USER/Документы/backup_pass";
---------------------------------------------------------------------------
backup_list =
{
-- backup critical --
{secure=true, zip=true, versions=true, source='~/.ssh/', dest='ssh' };
{secure=true, zip=true, versions=true, source='~/.gnupg/', dest='gpg' };
{secure=true, zip=true, mirror=true, source='~/.config/', dest='cfg' };
{secure=true, zip=true, mirror=true, source='~/.mozilla/', dest='firefox'};
{secure=true, zip=true, mirror=true, source='~/Документы/',dest='doc' };
-- diffrerent media --
{secure=false,zip=true, mirror=true, source='~/Книги/', dest='book'};
{secure=false,zip=true, mirror=true, source='~/Музыка/',dest='music'};
-- backup multimedia --
{secure=false, zip=false, versions=false, source='~/FAMILY_ARCHIVE/', dest='family'};
-- backup dev stuff --
{secure=false, zip=false, versions=true, source='~/.gitconfig',dest='gitconfig'};
{secure=false, zip=false, versions=true, source='~/.vimrc', dest='vimrc'};
{secure=false, zip=false, versions=true, source='~/.bashrc', dest='bashrc'};
{secure=false, zip=true, versions=true, source='~/.vim/', dest='vim'};
-- backups source code --
{secure=false, zip=true, mirror=true, source='/mnt/STORAGE/', dest='code'};
};
};
-------------------------------------------------------------------------------
-- :secure=true - шифровать ли сжатый бекап с паролем.
-- создать архив и запаролить, zip должен быть zip=true
-- явное указание zip=false или отсуцтвие ключа создаст ошибку
-- ----------------------------------------------------------------------------
-- :secure=false - тоже самое поведение что и при zip=false
-- ----------------------------------------------------------------------------
-- :zip=true - создать архив и добавить в него файлы если их нет в архиве
-- если version=false то обновлять архив, добавляя новые
-- файлы и обновля существующие, не удаляет отсуцтвующие
-------------------------------------------------------------------------------
-- :zip=false - создавать бекап в каталоге dest если versions=false
-- то клонировать source в dest, при повторном бекапе добавлять
-- новые файлы и обновлять текущие, не удалять отсуцтвующие
-- если versions=true создавать отдельный бекап в новом
-- каталоге с датой бекапа, полностью отельный бекап.
-------------------------------------------------------------------------------
-- :versions=true - если zip=false то создать новый каталог с датой бекапа
-- если zip=true то создать новый архив с датой бекапа
-------------------------------------------------------------------------------
-- :mirror=true - если zip=true то создаётся точная сжатая копия источника
-- если при этом versions=false то текущий архив удаляет из
-- себя файлы которых больше нет в источнике
-- если zip=false то вместо архива создаётся точная копия
-- источника с удалением файлов из каталога бекапа если их
-- нет в источнике, тоже самое зеркалирование/клонирование
-------------------------------------------------------------------------------
-- :source - файл или каталог который нужно бекапить, каталоги
-- обрабатываются рекурсивно бекапя всё что есть
-------------------------------------------------------------------------------
-- :dest - каталог внутри носителя для бекапа, оно же префикс
-- добавляющийся к каталогам и/или архивам при versions=true
-- отражает то бекап чего делается ОБЯЗАН БЫТЬ УНИКАЛЬНЫМ!!!!
-------------------------------------------------------------------------------
-- Если какого либо ключа нет то он считается равным false
-------------------------------------------------------------------------------
-- В случае если zip mkdir или rsync завершаться с кодом отличным от нуля
-- бекап тут же прекращается с уведомлением на рабочий стол и в лог ошибок
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
---- исполняет все команды, в случае ошибки прерывает всю работу
-------------------------------------------------------------------------------
local function execute(rule,command,exit_if_fail)
local success, meta, code = os.execute(command..' >> backup-info.log');
if (not success) then
-- hide pass from output
-- info log and messages
if rule.backup_pass then
command = command:gsub(rule.backup_pass,"********");
end
local msg = string.format('BACKUP ERROR %s %s %s',command,meta,code);
io.stderr:write(msg..'\n');
io.open('backup-error.log','a+'):write(msg);
execute({},'notify-send "'..msg..'" -u critical',false);
if (exit_if_fail == true) then
os.exit(127);
end
return success;
end
return success;
end
-------------------------------------------------------------------------------
--- информирует об ошибке и прерывает всю работу
-------------------------------------------------------------------------------
local function backup_fail(msg)
io.stderr:write(msg..'\n');
io.open('backup-error.log','a+'):write('\n'..msg..'\n');
execute({},'notify-send -u critical "'..msg..'"',false);
os.exit(127);
end
-------------------------------------------------------------------------------
--- просто информирует
-------------------------------------------------------------------------------
local function backup_info(msg)
io.stdout:write(msg..'\n');
local log = io.open('backup-info.log','a+');
log:write('\n'..msg..'\n');
log:close();
execute({},'notify-send -u normal "'..msg..'"',false);
end
-------------------------------------------------------------------------------
--- проверяет используется ли в конфигурации пароль
-------------------------------------------------------------------------------
function check_need_pass(rule)
for name,val in pairs(rule.backup_list) do
if val.secure and val.secure == true then
return true;
end
end
return false;
end
-------------------------------------------------------------------------------
--- делает бекап K.O. :)
-------------------------------------------------------------------------------
local function make_backup(rule)
if (rule.backup_path) then
-- replaice $USER in path if we have ite
rule.backup_path = rule.backup_path:gsub('$USER',os.getenv('USER'));
else
backup_fail('BACKUP ERROR backup_path not set, no place to save backup',true);
end
-- we use password?
if (check_need_pass(rule) == true) then
-- check pass contained in configuration
if not rule.backup_pass and not rule.backup_pass_file then
backup_fail('BACKUP ERROR you use secure option but backup_pass or backup_pass_file no set!',true);
end
-- if pass_file set read the password
-- and override/write rule.backup_pass
if (rule.backup_pass_file) then
-- replaice $USER in path if we have ite
rule.backup_pass_file = rule.backup_pass_file:gsub('$USER',os.getenv('USER'));
local pass_file = io.open(rule.backup_pass_file);
if (pass_file) then
if rule.backup_pass then
backup_info('BACKUP WARNING rewrite backup_pass from backup_pass_file');
end
rule.backup_pass = pass_file:read('*l');
else
backup_fail('BACKUP ERROR '..rule.backup_pass_file..' no readable!',true);
end
pass_file:close();
end
-- maybe pass use only numbers force to string
rule.backup_pass = tostring(rule.backup_pass);
-- check small pass, no need but jut be
if not rule.backup_pass or #rule.backup_pass <= 4 then -- kek cheburek
backup_info('BACKUP WARNING you backup_pass small, less or equal 4 symbols');
end
end
-- info about starting
backup_info("BACKUP START "..os.date());
--first check, we have storage and access?
execute(rule,'mkdir -p '..rule.backup_path, true);
-- lockfile + uuid for exclude collisions
local lockfile = '.lock-938448fc-1742-43e9-bff8-e2acf6d29710';
-- check/create lock file
-- if backup failed you self
-- delete lock file your hands
if not io.open(rule.backup_path..'/'..lockfile,'r') then
io.open(rule.backup_path..'/'..lockfile,'w'):write('lock');
else
backup_fail("OTHER BACKUP PROCESS IS WORKING, SKIPPING THIS PROCESS\n"..
"IF YOU SURE DELETE LOCK FILE "..rule.backup_path..'/'..lockfile..'\n',true);
----------------------------------------------------------------------
-- uncomment if after backup error no need locked backup for fix error
----------------------------------------------------------------------
-- os.remove(rule.backup_path..'/'..lockfile);
end
for id,item in pairs(rule.backup_list) do
local date = os.date("%Y-%m-%d_%X");
local path = rule.backup_path;
local pass = rule.backup_pass;
local dest = item.dest;
local source = item.source;
if (not source) then
backup_fail('BACKUP ERROR source path not set, what files backup?',true)
end
if (not dest) then
backup_fail('BACKUP ERROR dest path not set, wheare dir name to save?',true)
end
execute(rule,'mkdir -p '..path..'/'..dest,true);
backup_info('BACKUP: '..source);
------------------- secure: versionize backup data ---------------------
------------------------------------------------------------------------
if (item.secure and item.zip and item.versions) then
local target = string.format('%s/%s/%s-%s.zip',path,dest,dest,date);
execute(rule,'zip -r9P '..pass..' '..target..' '..source,true);
goto next_item;
end
---------------- secure: update and append backup data -----------------
------------------------------------------------------------------------
if (item.secure and item.zip and not item.versions) then
local target = string.format('%s/%s/%s.zip',path,dest,dest);
local opts = item.mirror and ' -rFS9P ' or ' -ru9P ';
execute(rule,'zip '..opts..' '..pass..' '..target..' '..source,true);
goto next_item;
end
----------- no secure: zipped update and append backup data ------------
------------------------------------------------------------------------
if (not item.secure and item.zip and not item.versions) then
local target = string.format('%s/%s/%s.zip',path,dest,dest);
local opts = item.mirror and ' -rFS9 ' or ' -ru9 ';
execute(rule,'zip '..opts..' '..target..' '..source,true);
goto next_item;
end
----- no secure: zipped, versionize update and append backup data ------
------------------------------------------------------------------------
if (not item.secure and item.zip and item.versions ) then
local target = string.format('%s/%s/%s-%s.zip',path,dest,dest,date);
execute(rule,'zip -r9 '..target..' '..source,true);
goto next_item;
end
--------- no secure: versionize update and append, no delete -----------
------------------------------------------------------------------------
if (not item.secure and not item.zip and item.versions ) then
local target = string.format('%s/%s/%s-%s/',path,dest,dest,date);
execute(rule,'mkdir -p '..target);
execute(rule,'rsync -arv '..source..' '..target,true);
goto next_item;
end
----------------- no secure: update and append, no delete --------------
------------------------------------------------------------------------
if (not item.secure and not item.zip and not item.versions) then
local target = string.format('%s/%s/',path,dest);
execute(rule,'mkdir -p '..target);
local opts = item.mirror and ' -arv --delete ' or ' -arv ';
execute(rule,'rsync '..opts..' '..source..' '..target,true);
goto next_item;
end
------------------------------------------------------------------------
if (item.secure and not item.zip) then
backup_fail('BACKUP ERROR UNSUPPORT SECURE WITCHOUT ZIP ARCHIVE :(');
end
::next_item::
end
backup_info("BACKUP END "..os.date());
os.remove(rule.backup_path..'/'..lockfile);
end
-- check depends
execute({},"zip --version",true);
execute({},"rsync --version",true);
--execute({},"mkdir --version",true);
execute({},"notify-send --version",true);
-- start backup now
make_backup(backup_rule)
Всё. Досвиданья.