LINUX.ORG.RU

Помогите со скриптом бэкапа

 ,


1

1

Сегодня заметил, что при таинственных обстоятельствах изменился конфиг какуна, который я писал пол ночи, помимо этого часто экспериментирую с конфигом nixos, окончательно созрев, передо мной встала задача организовать простое резервное копирование конфигов и своих исходников, путём отправки пути к файлу или каталогу в файл из которого автоматизированно будет это резервное копирование и совершаться. Помимо этого дерево диффов позволяет отследить время изменения каждого из файлов, интервал в 3 часа, думаю, сильно упростит поиск по логам. Я накидал скрипт, но с bash особо не знаком, прошу указать на ошибки, которые могут привести к ошибкам копирования, хочется рулить только списком и спасть спокойно. Заранее спасибо.

#checking PID
PID_FILE="./sync.pid"
if [ -f "$PID_FILE" ] && kill -0 `cat "$PID_FILE"` 2> /dev/null &&  ps -p `cat "$PID_FILE"` -o cmd | grep `basename $0`
  then
    exit 1
  else
    echo $$ > "$PID_FILE"
fi
#dir for backup process
mkdir "$1" 2> /dev/null
#archive name
name=`date +%H_%j_%y`
#first path backup dir
#second file with list synced files, one on line
if [ $# -ne 2 ]; then
  echo "err"
  exit 1
fi
#mount /dev/sda5 "$1"
cd "$1"
#actual synced files
mkdir sync 2> /dev/null
#archives of diffs
mkdir archives 2> /dev/null
#lists of synced files and logs
mkdir lists 2> /dev/null
#tree of diffs
mkdir diffs 2> /dev/null
#retrive list of synced files
rsync --progress -c -a -n --files-from="$2" / ./sync/ > "./$name.tmp"
tail -n +3 "./$name.tmp" > "./lists/$name.list" && rm "./$name.tmp"
#count synced files
count=$(grep -v '/$' "./lists/$name.list" | wc -l)
#skip if no files need to sync
if [ "$count" -gt 0 ]; then
  #temp dir
  mkdir "./$name" 2> "./lists/$name.log"
  while IFS='' read -r line || [[ -n "$line" ]]; do
    if [ -f "./sync/$line" ]; then
      mkdir -p "./$name/$line" 2>> "./lists/$name.log"
      #dir for diff named by full path and name of changed file
      mkdir -p "./diffs/$line" 2>> /dev/null
      #make diff file
      diff "/$line" "./sync/$line" > "./$name/$line/$name.diff"
      #copy from temp to diff tree
      cp "./$name/$line/$name.diff" "./diffs/$line/$name.diff" 2>> "./lists/$name.log"
    fi
  done < "./lists/$name.list"
  #make archive with current diffs
  tar -zcf "./archives/$name.tar.gz" "./$name" 2>> "./lists/$name.log"
  #put to mega.nz
  megaput --path=/Root/one --no-progress "./archives/$name.tar.gz" 2>> "./lists/$name.log"
  #remove temp dir
  rm -r "./$name" 2>> "./lists/$name.log"
fi
#real sync
rsync -q -c -a --files-from="$2" / ./sync/ 2>> "./lists/$name.log"
#if monday create full archive and put to mega
if [[ `date +%u%H%M` < 10300 ]]; then
  tar -zcf "./archives/weekly_$name.tar.gz" ./sync 2>> "./lists/$name.log"
  megaput --path=/Root/one --no-progress "./archives/weekly_$name.tar.gz" 2>> "./lists/$name.log"
fi
#umount "$1"
rm "$PID_FILE"
echo "$count"
exit 0

PS Скрипт будет запускаться раз в 3 часа. Что недельный бэкап может не случиться я вкурсе, подскажите как правильно. Плюсом хочу в этом скрипте монтировать отдельный раздел в /bu вначале и размонтировать вконце, чтобы в промежутках никто не мог туда писать, но пока свободного раздела для тестов нет.

PPS Возможно данная тема будет полезна не только лишь мне.

★★★★

Последнее исправление: ddidwyll (всего исправлений: 8)

Ошибок копирования я здесь не вижу, но так нужен какой-то комментарий, показывающий структуру создаваемых каталогов и проверки exit code команд. ″cd″ без проверки — если нет каталога /bu будет непонятно что. Нет блокировки от одновременного запуска нескольких экземпляров скрипта. Непонятно зачем переменная ″count″, она всё одно не выходит из sub-shell, в котором работает ″read″.

ИМХО, если rsync не нашёл различий, то просто делать запись в лог и выход из скрипта, не создавать каталог «$name».

mky ★★★★★
()

Как уже было отмеченно, не хватает проверки на уже запущенный скрипт. Можно прикрутить что-то такое...

### Startup checking PID
PID_FILE="/path/to/pid"
if [ -f $PID_FILE ] && kill -0 `cat $PID_FILE` 2> /dev/null &&  ps -p `cat $PID_FILE` -o cmd | grep `basename $0`
  then
   exit 1
  else
   echo $$ > $PID_FILE
fi
###

FluffyPillow
()

Так же желательно сделать проверку rsync не по времени, а по хэшам. Ключ -c в помощь. Ну и проверки на наличие директорий и файлов... Зачем вообще cd? Можно абсолютные пути указывать от корня.

FluffyPillow
()

mky FluffyPillow

Подредактировал немного, для ясности добавил комментарии а ля мутко. Скорее всего добавил ошибок, синтаксис баша меня скукоживает, надо было на го писать, всё веселей.

ddidwyll ★★★★
() автор топика

Зачем вообще cd? Можно абсолютные пути указывать от корня

Хз, кажется так удобнее и нагляднее.

Ключ -c в помощь.

Точно, совсем забыл про него.

проверки на наличие директорий и файлов...
без проверки — если нет каталога

Такой как сейчас вариант норм? Выглядит плохо, но городить проверки на каждую директорию тоже не хочется.

Нет блокировки от одновременного запуска нескольких экземпляров скрипта.
Можно прикрутить что-то такое...

Скопипастил, спасибо, вроде работает, лень вникать.

Непонятно зачем переменная ″count″, она всё одно не выходит из sub-shell, в котором работает ″read″.

Странно, у меня и в первом варианте правильно отображалось количество. Счетчик хочу выводить в i3blocks, им же собственно скрипт и запускать, есть подводные камни?

ddidwyll ★★★★
() автор топика
Ответ на: комментарий от ddidwyll

Стало страшнее. Я ожидал, что в начале будет один многострочный комментарий, где будет изображено дерево каталогов, чтобы было понятно что где лежит, а создание каталогов объеденить в одну команду — mkdir понимает несколько аргументов.

Я писал только про проверку ″cd /bu″, что если у скрипта не получилось зайти в рабочий каталог, он прекращал работу, а не пытался насоздавать файлов непонятно где.

В отношении ″$# -ne 2″, то принято писать как-то так:

if [ $# -ne 2 ]; then
  echo "Usage: $0 DIRECTORY FILE";
  echo "Backup files from list in FILE to DIRECTORY"
  exit 1
fi >&2

Скопипастил, спасибо, вроде работает, лень вникать.

Ну и хорошо, а то там такой ужос с рейсами, лучше уж использовать команду flock на ваш рабочий каталог $1, как-то так:

exec 200< ./$1 || exit 1
if ! flock -n 200 ; then
        echo "Pid $$. Can't Lock."
        exit 2
fi
echo "Pid $$. Lock Ok"
И по завершению скрипта и всех порождённых им процессов лок сам исчезает. Поэтому не нужно городить огород с проверкой наличия процесса с pid из файла.

mky ★★★★★
()

Какой-то большой и сложный скрипт. Почему нельзя было в одну строку с помощью rsync?

UVV ★★★★★
()

Ещё можно inotify использовать для отслеживания источника на наличие изменений и когда они произойдут запускать rsync. И крон не нужен, и бэкап сразу же после изменения источника будет.

FluffyPillow
()

while read -r line || [[ -n «$line» ]]; do

Это что-то новенькое. И не могу понять, какой смысл. Скорее всего никакого.

vodz ★★★★★
()

Я делаю намного проще.

Монтирую с бэкап сервера каталог по sshfs и потом копирую что нужно туда.

slamd64 ★★★★★
()
Ответ на: комментарий от slamd64

Монтирую с бэкап сервера каталог по sshfs и потом копирую что нужно туда.

Rsync с ключом -с очень медленно работает по sshfs, простым tar архивировать если только.

FluffyPillow
()
Ответ на: комментарий от FluffyPillow

Именно простым таром и жму сразу в подмонтированный SSHFS. Быстро работает.

rsync вообще медленно работает именно потому, что он на каждый файл открывает новую сессию (в случае SSH/SFTP/SCP).

Если куча мелких файлов, то реальная скорость rsync'а падает до 150-200Кб/с и меньше. Доводилось мне как-то переливать 87Гб файлопомойки одной организации. С многочисленными перезапусками-обновлениями-докачками заняло 3 недели.

slamd64 ★★★★★
()
Ответ на: комментарий от UVV

Он хочет получать и резервную копию, и diff, и архив предыдущих версий, и заливку этого архива на файлообменник.

mky ★★★★★
()
Ответ на: комментарий от slamd64

Мой 5 гиговый /home бэкапится в течение часа, емнип. Делаю через rsync + ssh. Или sshfs - это что-то другое?

UVV ★★★★★
()
Ответ на: комментарий от mky

как-то так

Спасибо, примерно понял как это работает, надо будет подробнее почитать.

ddidwyll ★★★★
() автор топика
Ответ на: комментарий от UVV

А как же промежуточные состояния? Например переписал большой метод, к примеру, а через некоторое время захотел посмотреть переписанный код, или я могу посмотреть изменения всех важных для меня конфигов в любой момент прошлого, все это ценой наименьшего размера бэкапов и полной автоматизацией. Rsync так не может.

ddidwyll ★★★★
() автор топика
Ответ на: комментарий от kerneliq

Почему нет? Я не использую VCS, для меня это самый удобный и простой способ контролировать фажные текстовые файлы, коих довольно много. Плюс чуток попрактиковался в bash.

ddidwyll ★★★★
() автор топика
Ответ на: комментарий от FluffyPillow

отслеживания источника на наличие изменений

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

ddidwyll ★★★★
() автор топика
Ответ на: комментарий от slamd64

Ну, у меня сохраняется только история изменений, не вижу способа автоматизировать мою задачу проще.

ddidwyll ★★★★
() автор топика
Ответ на: комментарий от slamd64

заняло 3 недели

Грустная история, что мешало запихать всё в архив или тарбол и качать тем же wget -c?

ddidwyll ★★★★
() автор топика
Ответ на: комментарий от UVV

sshfs - удаленная фс средствами ssh которая монтируется локально.

FluffyPillow
()
Ответ на: комментарий от mky

лучше уж использовать команду flock

У вас получилось что-то странное. $1 в данном случае что? А сообщения вы выводите о pid...

vodz ★★★★★
()
Ответ на: комментарий от ddidwyll

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

Тогда ты изобрёл велосипед

UVV ★★★★★
()

Зачем велосипедить при живом rsnapshot?

dexpl ★★★★★
()

сделай локальную git репу

или на гит свой залей

проще будет

smilessss ★★★★★
()
Ответ на: комментарий от ddidwyll

Удаленка на другой стороне Шарика и недостаток места на сервере.

slamd64 ★★★★★
()
Ответ на: комментарий от UVV

Зависит-то от того, какого размера у тебя там файлы.

Те 87Гб - в основном pdf, doc, jpg, png размерами от нескольких килобайт до мегабайта. Причем, в большинстве своем около 50Кб.

То есть, порядка 1.5 миллионов файлов.

slamd64 ★★★★★
()
Ответ на: комментарий от vodz

В случае скрипта ТС, $1 это рабочий каталог. А сообщение просто как пример кода, чтобы было понятно, какой код выполняется если лок не получился.

mky ★★★★★
()
Ответ на: комментарий от mky

А сообщение просто как пример кода, чтобы было понятно, какой код выполняется если лок не получился.

Пример использования flock есть в man, вопрос был в вашем "./$1" . Даже если это и был параметр у функции с содержимым pid, то совершенно не понятно, зачем тут "./"

vodz ★★★★★
()
Ответ на: комментарий от vodz

В man один пример с файлом и на часть скрипта (subshell), здесь другой, на весь скрипт и на каталог.

Даже если это и был параметр у функции с содержимым pid

Это параметры (аргументы) скрипта, см. код в стартовом посте:
#first path backup dir
#second file with list synced files, one on line

И echo $$ там.

зачем тут "./"

Чтобы копипаста не работала и чтобы ТС

чуток попрактиковался в bash.

и подумал, что такое рабочий каталог момент запуска скрипта, и почему:

PID_FILE="./sync.pid"
неправильно.

mky ★★★★★
()
Ответ на: комментарий от mky

В man один пример

Странно, у меня там два :) И это правильно, ибо с subshell мало когда удобно.

здесь другой, на весь скрипт и на каталог.

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

vodz ★★★★★
()
Последнее исправление: vodz (всего исправлений: 1)

Поддерживаю git, там это всё есть и велосипеды городить не надо.

Pyzia ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.