Инструменты пользователя

Инструменты сайта


raspberry_pi:pi_4_model_b:raspberry_pi_os:bash:scripts:backup

Скрипт резервного копирования для Debian

После установки Vaultwarden на Raspberry Pi 4 Model B с Raspberry Pi OS 11 (bullseye) остро встала задача надежного резервирования, в первую очередь, базы данных и, заодно, всяких разных файлов конфигураций. Немного поискав готовые решения, аля «галочки поставил и забыл», расстроился и понял, что это нам не Винда и, или искать готовый скрипт, или писать его самому…

Искать готовый, рабочий и выполняющий то, что нужно именно мне без размахивания напильником, это еще та задача, поэтому было решено немного попрактиковаться в быдлокодинге1) и сделать, как обычно, все самому.

Возможности

  1. Скрипт запускается автоматически cron'ном или вручную2);
  2. может резервировать файлы, папки и базы MySQL/SQLite3);
  3. архивирует и шифрует данные, используя 7zip;
  4. хранит 7 ежедневных и 12 ежемесячных полных резервных копий4);
  5. может отправлять архивы по протоколу SFTP5) на удаленный сервер;
  6. может оповещать о результатах и об ошибках по электронной почте.

Совместимость

Скрипт проверен на Raspberry Pi OS 11 (bullseye) x64 с командами следующих версий:

  • bash 5.1.4;
  • mysqldump 10.196);
  • sqlite3 3.34.1;
  • 7za 16.02;
  • curl 7.74.0;
  • mail7) 3.10;
  • ssmtp 2.64.

Настройка

:!: Если предполагается использование уведомлений по электронной почте, предварительно необходимо настроить клиент ssmtp!

  1. Для удобства авторизуемся под суперпользователем

    sudo -s
  2. устанавливаем зависимости

    apt update && apt install sqlite3 p7zip-full curl
  3. создаем и защищаем необходимые каталоги

    mkdir -pv /opt/backupscr/assets/ /opt/backupscr/sources/ && chmod -Rv 750 /opt/backupscr/
  4. открываем файл скрипта и добавляем в него код из раздела ниже

    nano /opt/backupscr/backup.sh
  5. разрешаем выполнение скрипта

    chmod +x /opt/backupscr/backup.sh
  6. добавляем файл настроек – все возможные настройки собраны тут!

    nano /opt/backupscr/assets/settings.txt
  7. добавляем файл перевода

    nano /opt/backupscr/assets/translation.txt
  8. добавляем файлы источников

    nano /opt/backupscr/sources/files.txt
    nano /opt/backupscr/sources/mysqldbs.txt
    nano /opt/backupscr/sources/sqlitedbs.txt
  9. добавляем задание cron

    echo "10 3 * * * root cd /opt/backupscr/ && ./backup.sh" | tee /etc/cron.d/server_backup

Для запуска по требованию необходимо выполнить следующую команду:

cd /opt/backupscr/ && ./backup.sh

Листинги

Весь код в достаточной мере прокомментирован, поэтому добавить мне нечего. =)

backup.sh

backup.sh
#!/bin/bash
 
# Written by Nikolay Soloshin (nikolay@soloshin.su) for Raspberry Pi OS 11 (bullseye) @ 2023.05
 
# Засекаем время выполнения скрипта
TIME_COUNTER=$(date +%s)
 
# Проверяем наличие необходимых разрешений
if [ $EUID -ne 0 ]; then
  echo "`date +%FT%H:%M:%S%:z` - Этот скрипт должен запускаться с правами суперпользователя. Выходим..."
  exit
else
  echo "`date +%FT%H:%M:%S%:z` - Не для протокола - скрипт запущен с правами суперпользователя!"
fi
 
# Проверяем наличие файла перевода
if [ -s ./assets/translation.txt ]; then
  source ./assets/translation.txt
  echo "`date +%FT%H:%M:%S%:z` - Не для протокола - файл перевода присутствует!"
else
  echo "`date +%FT%H:%M:%S%:z` - Файл перевода отсутствует или пуст! Выходим..."
  exit
fi
 
# Проверяем наличие файла настроек
if [ -s ./assets/settings.txt ]; then
  source ./assets/settings.txt
  echo "`date +%FT%H:%M:%S%:z` - ${LANG_PR_SETTINGS}"
else
  echo "`date +%FT%H:%M:%S%:z` - ${LANG_NO_SETTINGS}"
  exit
fi
 
# Определяем тип сегодняшнего задания - годовое или недельное
if [ `date +%-d` -eq ${DAY_OF_MONTH} ]; then
  ARCHIVE_NAME="moy-`date +%m`" # Month Of the Year
  LANG_NOW_IS=${LANG_NOW_MONTH}
else
  ARCHIVE_NAME="dow-`date +%u`" # Day Of the Week
  LANG_NOW_IS=${LANG_NOW_WEEK}
fi
 
# Проверяем и, если необходимо, создаем папку лог-файлов
if ! [ -d ${LOG_PATH} ]; then
  echo "`date +%FT%H:%M:%S%:z` - ${LANG_LOG_DIR}"
  mkdir -pv ${LOG_PATH} > /dev/null
  echo "`date +%FT%H:%M:%S%:z` - ${LANG_LOG_DIR}" &> ${LOG_PATH}/${ARCHIVE_NAME}.log
else
  echo "`date +%FT%H:%M:%S%:z` - ${LANG_LOG_DIR_PRES}" | tee ${LOG_PATH}/${ARCHIVE_NAME}.log
fi
LOG_PATH="${LOG_PATH}/${ARCHIVE_NAME}"
 
# Перенаправляем вывод ошибок в файл
echo "`date +%FT%H:%M:%S%:z` - ${LANG_STDERR}" | tee -a ${LOG_PATH}.log
exec 2> ${LOG_PATH}-errors.log
 
# Проверяем наличие необходимых команд в системе, если чего-то нет - выходим
echo "`date +%FT%H:%M:%S%:z` - ${LANG_COMMANDS_TEST}" | tee -a ${LOG_PATH}.log
COMMANDS_COUNTER=( 0 0 )
echo -n "`date +%FT%H:%M:%S%:z` -" | tee -a ${LOG_PATH}.log
for i in ${COMMANDS_FOR_TEST[@]}; do
  echo -n " $i" | tee -a ${LOG_PATH}.log
  if [ `command -v $i` ]; then
    echo -n " ✓" | tee -a ${LOG_PATH}.log
    (( COMMANDS_COUNTER[0]++ ))
  else
    echo -n " ✗" | tee -a ${LOG_PATH}.log
    (( COMMANDS_COUNTER[1]++ ))
  fi
done
echo | tee -a ${LOG_PATH}.log
COMMANDS_COUNTER[2]=$((${COMMANDS_COUNTER[1]}+${COMMANDS_COUNTER[0]}))
if [ ${COMMANDS_COUNTER[1]} -gt 0 ]; then
  echo "`date +%FT%H:%M:%S%:z` - ${LANG_COMMANDS_FAIL} (${COMMANDS_COUNTER[1]} ${LANG_COMMANDS_OF} ${COMMANDS_COUNTER[2]})! ${LANG_COMMANDS_EXIT}" | tee -a ${LOG_PATH}.log
  exit
else
  echo "`date +%FT%H:%M:%S%:z` - ${LANG_COMMANDS_OK} (${COMMANDS_COUNTER[0]} ${LANG_COMMANDS_OF} ${COMMANDS_COUNTER[2]})! ${LANG_COMMANDS_CONTINUE}" | tee -a ${LOG_PATH}.log
fi
 
# Проверяем наличие временной папки и, если ее нет, создаем и устанавливаем права
if ! [ -d ${DESTINATION_PATH}/temp ]; then
  echo "`date +%FT%H:%M:%S%:z` - ${LANG_DEST_DIR}" | tee -a ${LOG_PATH}.log
  mkdir -pv ${DESTINATION_PATH}/temp >> ${LOG_PATH}.log
  echo "`date +%FT%H:%M:%S%:z` - ${LANG_DEST_CHMOD}" | tee -a ${LOG_PATH}.log
  chmod -Rv ${ACCESS_RIGHTS} ${DESTINATION_PATH} >> ${LOG_PATH}.log
else
  echo "`date +%FT%H:%M:%S%:z` - ${LANG_DEST_DIR_PRES}" | tee -a ${LOG_PATH}.log
fi
 
# Устанавливаем начальные значения системных переменных
NOTHING_TO_DO=0
LOG_COMPOSITION=( 1 1 1 )
 
# Копируем файлы по списку; если список пуст или отсутствует, сообщаем об этом
if [ -s ${SOURCES_PATH}/${SOURCES_LIST} ]; then
  echo "`date +%FT%H:%M:%S%:z` - ${LANG_COPY_FILES}" | tee -a ${LOG_PATH}.log
  for i in $(cat ${SOURCES_PATH}/${SOURCES_LIST}); do
    cp -rv --parents $i ${DESTINATION_PATH}/temp >> ${LOG_PATH}.log
  done
else
  echo "`date +%FT%H:%M:%S%:z` - ${LANG_THE_FILE} \"${SOURCES_LIST}\" ${LANG_NOT_PRES} ${LANG_SKIP_FILES}" | tee -a ${LOG_PATH}.log
  (( NOTHING_TO_DO++ ))
  LOG_COMPOSITION[0]=0
fi
 
# Создаем дамп MySQL по списку; если список пуст или отсутствует, сообщаем об этом
if [ -s ${SOURCES_PATH}/${MYSQL_DB_LIST} ]; then
  echo "`date +%FT%H:%M:%S%:z` - ${LANG_SQL_DUMP}" | tee -a ${LOG_PATH}.log
  for i in $(cat ${SOURCES_PATH}/${MYSQL_DB_LIST}); do
    echo "`date +%FT%H:%M:%S%:z` - ${LANG_SQL_DUMP_NOW} \"$i\"..." | tee -a ${LOG_PATH}.log
    mysqldump $i -u ${MYSQL_USER} -p="${MYSQL_PASSWORD}" > ${DESTINATION_PATH}/temp/$i.sql
  done
else
  echo "`date +%FT%H:%M:%S%:z` - ${LANG_THE_FILE} \"${MYSQL_DB_LIST}\" ${LANG_NOT_PRES} ${LANG_SKIP_SQL}" | tee -a ${LOG_PATH}.log
  (( NOTHING_TO_DO++ ))
  LOG_COMPOSITION[1]=0
fi
 
# Создаем дамп SQLite по списку; если список пуст или отсутствует, сообщаем об этом
if [ -s ${SOURCES_PATH}/${SQLITE_DB_LIST} ]; then
  echo "`date +%FT%H:%M:%S%:z` - ${LANG_SQLITE_VC}" | tee -a ${LOG_PATH}.log
  for i in $(cat ${SOURCES_PATH}/${SQLITE_DB_LIST}); do
    n=$(dirname $i)
    echo "`date +%FT%H:%M:%S%:z` - ${LANG_SQLITE_DIR} \"$n\"..." | tee -a ${LOG_PATH}.log
    mkdir -pv ${DESTINATION_PATH}/temp/${n} >> ${LOG_PATH}.log
    echo "`date +%FT%H:%M:%S%:z` - ${LANG_SQLITE_VC_NOW} \"`basename $i`\"..." | tee -a ${LOG_PATH}.log
    sqlite3 $i "VACUUM INTO '${DESTINATION_PATH}/temp/$i.vacuum'" >> ${LOG_PATH}.log
  done
else
  echo "`date +%FT%H:%M:%S%:z` - ${LANG_THE_FILE} \"${SQLITE_DB_LIST}\" ${LANG_NOT_PRES} ${LANG_SKIP_SQLITE}" | tee -a ${LOG_PATH}.log
  (( NOTHING_TO_DO++ ))
  LOG_COMPOSITION[2]=0
fi
 
# Если отсутствуют или пусты все три файла, сообщаем и выходим
[ ${NOTHING_TO_DO} -eq 3 ] && { echo "`date +%FT%H:%M:%S%:z` - ${LANG_NOTHING}" | tee -a ${LOG_PATH}.log; exit; }
 
# Копируем основной лог-файл во временную папку
echo "`date +%FT%H:%M:%S%:z` - ${LANG_COPY_LOG}" | tee -a ${LOG_PATH}.log
cp -v ${LOG_PATH}.log ${DESTINATION_PATH}/temp/general.log >> ${LOG_PATH}.log
 
# Если есть ошибки, копируем лог-файл ошибок во временную папку
[ -s ${LOG_PATH}-errors.log ] && cp -v ${LOG_PATH}-errors.log ${DESTINATION_PATH}/temp/errors.log >> ${LOG_PATH}.log
 
# Сообщаем о типе сегодняшней архивации
echo "`date +%FT%H:%M:%S%:z` - ${LANG_NOW_IS}" | tee -a ${LOG_PATH}.log
 
# Удаляем старый архив за этот день или месяц, если ошибка (архива нет) - молчим
echo "`date +%FT%H:%M:%S%:z` - ${LANG_DELETE_ARC}" | tee -a ${LOG_PATH}.log
rm -vf ${DESTINATION_PATH}/backup-${ARCHIVE_NAME}.7z >> ${LOG_PATH}.log
 
# Создаем новый архив с паролем
echo "`date +%FT%H:%M:%S%:z` - ${LANG_NEW_ARC}" | tee -a ${LOG_PATH}.log
7za a -mhe=on -p${ZIP_PASSWORD} -bd -bb -t7z ${DESTINATION_PATH}/backup-${ARCHIVE_NAME}.7z ${DESTINATION_PATH}/temp/* >> ${LOG_PATH}.log
 
# Если нет ошибок, извлекаем из лог-файлов статистику
ARCHIVE_SIZE=$(cat ${LOG_PATH}.log | grep -F "Archive size" | sed -e "s/Archive size:/${LANG_ARCHIVE_SIZE}/" -e "s/bytes/${LANG_ARCHIVE_BYTES}/")
ARCHIVE_COMPOSITION=$(cat ${LOG_PATH}.log | grep -P "folders.*files" | sed -e "s/folders/${LANG_ARCHIVE_FOLDERS}/" -e "s/files/${LANG_ARCHIVE_FILES}/" -e "s/bytes/${LANG_ARCHIVE_BYTES}/")
echo "`date +%FT%H:%M:%S%:z` - ${LANG_ARCHIVE_CREATED} \"backup-${ARCHIVE_NAME}.7z\" ${ARCHIVE_SIZE}!" | tee -a ${LOG_PATH}.log
echo "`date +%FT%H:%M:%S%:z` - ${LANG_ARCHIVE_IN} ${ARCHIVE_COMPOSITION}." | tee -a ${LOG_PATH}.log
 
# Если разрешено, отправляем архив на SFTP-сервер, или сообщаем об обратном
if [ ${SFTP_DISABLE} = "no" ]; then
  echo "`date +%FT%H:%M:%S%:z` - ${LANG_SEND_ARC}" | tee -a ${LOG_PATH}.log
  curl -ks sftp://${SFTP_SERVER}/~/${SFTP_DIRECTORY}/ --ftp-create-dirs -u ${SFTP_USER}:${SFTP_PASSWORD} -T ${DESTINATION_PATH}/backup-${ARCHIVE_NAME}.7z --ftp-create-dir --show-error >> ${LOG_PATH}.log
else
  echo "`date +%FT%H:%M:%S%:z` - ${LANG_SEND_DISABLE}" | tee -a ${LOG_PATH}.log
fi
 
# Очищаем временную папку
echo "`date +%FT%H:%M:%S%:z` - ${LANG_CLEAR_DEST}" | tee -a ${LOG_PATH}.log
rm -rv ${DESTINATION_PATH}/temp/* >> ${LOG_PATH}.log
 
# Считаем, сколько выполнялся скрипт
TIME_COUNTER=$(( `date +%s` - ${TIME_COUNTER} ))
echo "`date +%FT%H:%M:%S%:z` - ${LANG_TIME_COUNTER}: ${TIME_COUNTER}." | tee -a ${LOG_PATH}.log
 
# Если разрешено, проверяем, если ли ошибки и, в зависимости от результата, отправляем тот или иной шаблон на почту
if [ -s ${LOG_PATH}-errors.log ]; then
  echo "`date +%FT%H:%M:%S%:z` - ${LANG_ERR_PRES}" | tee -a ${LOG_PATH}.log
  if [ ${EMAIL_DISABLE} = "no" ]; then
    echo "`date +%FT%H:%M:%S%:z` - ${LANG_SEND_EMAIL}" | tee -a ${LOG_PATH}.log
    cat ${LOG_PATH}-errors.log | mail -s "${LANG_ERR_SUBJ}" ${EMAIL_RECIPIENT} -a "Content-Type: text/plain; charset=UTF-8" -a "From: \"${SENDER_NAME}\" <${EMAIL_SENDER}>" >> ${LOG_PATH}.log
  else
    echo "`date +%FT%H:%M:%S%:z` - ${LANG_EMAIL_DISABLE}" | tee -a ${LOG_PATH}.log
  fi
else
  echo "`date +%FT%H:%M:%S%:z` - ${LANG_NO_ERR}" | tee -a ${LOG_PATH}.log
  if [ ${EMAIL_IF_OK} = "yes" ] && [ ${EMAIL_DISABLE} = "no" ]; then
    echo "`date +%FT%H:%M:%S%:z` - ${LANG_STILL_SEND}" | tee -a ${LOG_PATH}.log
    {
      echo "${LANG_ARCHIVE_CREATED} \"backup-${ARCHIVE_NAME}.7z\" ${ARCHIVE_SIZE}!"
      echo "${LANG_ARCHIVE_IN} ${ARCHIVE_COMPOSITION}."
      echo
      echo "${LANG_TIME_COUNTER}: ${TIME_COUNTER}."
      [ ${LOG_COMPOSITION[0]} -eq 1 ] && { echo; echo "${LANG_EMAIL_FILES}:"; cat ${SOURCES_PATH}/${SOURCES_LIST}; }
      [ ${LOG_COMPOSITION[1]} -eq 1 ] && { echo; echo "${LANG_EMAIL_DBS} (MySQL):"; cat ${SOURCES_PATH}/${MYSQL_DB_LIST}; }
      [ ${LOG_COMPOSITION[2]} -eq 1 ] && { echo; echo "${LANG_EMAIL_DBS} (SQLite):"; cat ${SOURCES_PATH}/${SQLITE_DB_LIST}; }
    } | mail -s "${LANG_OK_SUBJ}" ${EMAIL_RECIPIENT} -a "Content-Type: text/plain; charset=UTF-8" -a "From: \"${SENDER_NAME}\" <${EMAIL_SENDER}>" >> ${LOG_PATH}.log
  elif [ ${EMAIL_IF_OK} = "yes" ] && [ ${EMAIL_DISABLE} = "yes" ]; then
    echo "`date +%FT%H:%M:%S%:z` - ${LANG_EMAIL_DISABLE}" | tee -a ${LOG_PATH}.log
  fi
fi
 
# Сообщаем, что все готово, можно завершаться!
echo "`date +%FT%H:%M:%S%:z` - ${LANG_ALL_DONE}" | tee -a ${LOG_PATH}.log
 
# With love from Vladivostok.

settings.txt

settings.txt
# Служебные директории
LOG_PATH="./logs/"
SOURCES_PATH="./sources/"
 
# Проверяемые перед запуском необходимые команды
COMMANDS_FOR_TEST=( "mysqldump" "sqlite3" "7za" "curl" "mail" )
 
# Директория для хранения резервных копий и временных файлов
DESTINATION_PATH="/tmp/localbackup/"
 
# Права доступа к директории "DESTINATION_PATH"
ACCESS_RIGHTS=700
 
# Названия файлов заданий
SOURCES_LIST="files.txt"
MYSQL_DB_LIST="mysqldbs.txt"
SQLITE_DB_LIST="sqlitedbs.txt"
 
# День месяца для ежемесячного резервного копирования (1-28)
DAY_OF_MONTH=3
 
# Настройки подключения к MySQL
MYSQL_USER="root"
MYSQL_PASSWORD="PaSZW0Rd"
 
# Ключевая фраза для архивов
ZIP_PASSWORD="StRong_%^&*#_paZZphraZe"
 
# Отключение отправки архива на удаленный сервер ("yes"|"no")
SFTP_DISABLE="yes"
 
# Настройки подключения к SFTP-серверу
SFTP_SERVER="server.zone:port"
SFTP_USER="user"
SFTP_PASSWORD="paZZWorD"
 
# Директория на сервере, создается автоматически ("dir"|"dir/dir")
SFTP_DIRECTORY="backups"
 
# Полное отключение оповещений ("yes"|"no")
EMAIL_DISABLE="yes"
 
# Настройки почтовых оповещений
EMAIL_RECIPIENT="admin@domain.zone"
EMAIL_SENDER="server@domain.zone"
SENDER_NAME="My Server"
 
# Оповещать даже если все хорошо ("yes"|"no")
EMAIL_IF_OK="yes"

translation.txt

translation.txt
LANG_PR_SETTINGS="Не для протокола - файл настроек присутствует!"
LANG_NO_SETTINGS="Файл настроек отсутствует или пуст! Выходим..."
LANG_LOG_DIR="Создаем директорию логов..."
LANG_LOG_DIR_PRES="Директория логов присутствует!"
LANG_STDERR="Перенаправляем stderr в лог-файл..."
LANG_COMMANDS_TEST="Проверяем наличие необходимых команд в системе..."
LANG_COMMANDS_FAIL="В системе нет некоторых команд"
LANG_COMMANDS_OK="Все команды есть в системе"
LANG_COMMANDS_OF="из"
LANG_COMMANDS_EXIT="Выходим..."
LANG_COMMANDS_CONTINUE="Продолжаем..."
LANG_DEST_DIR="Создаем временную директорию..."
LANG_DEST_CHMOD="Устанавливаем права доступа к директории резервных копий..."
LANG_DEST_DIR_PRES="Временная директория присутствует!"
LANG_COPY_FILES="Копируем файлы из источников во временную директорию..."
LANG_THE_FILE="Файл"
LANG_NOT_PRES="отсутствует или пуст!"
LANG_SKIP_FILES="Пропускаем копирование файлов..."
LANG_SQL_DUMP="Создаем дампы баз MySQL во временной директории..."
LANG_SQL_DUMP_NOW="Сейчас создается дамп базы"
LANG_SKIP_SQL="Пропускаем создание дампа баз MySQL..."
LANG_SQLITE_VC="Создаем дампы баз SQLite во временной директории..."
LANG_SQLITE_DIR="Создаем директорию"
LANG_SQLITE_VC_NOW="Сейчас создается дамп базы"
LANG_SKIP_SQLITE="Пропускаем создание дампа баз SQLite..."
LANG_NOTHING="Делать нечего, выходим..."
LANG_COPY_LOG="Копируем лог-файлы во временную директорию..."
LANG_NOW_MONTH="Сегодня ежемесячная архивация!"
LANG_NOW_WEEK="Сегодня ежедневная архивация!"
LANG_DELETE_ARC="Удаляем старый локальный архив..."
LANG_NEW_ARC="Архивируем файлы из временной директории..."
LANG_ARCHIVE_CREATED="Создан архив"
LANG_ARCHIVE_IN="В архиве"
LANG_ARCHIVE_SIZE="размером"
LANG_ARCHIVE_FOLDERS="папок"
LANG_ARCHIVE_FILES="файлов"
LANG_ARCHIVE_BYTES="байт"
LANG_SEND_ARC="Отправляем архив на удаленный сервер..."
LANG_SEND_DISABLE="Отправка архива на удаленный сервер отключена!"
LANG_CLEAR_DEST="Очищаем содержимое временной директории..."
LANG_TIME_COUNTER="Время выполнения скрипта (в секундах)"
LANG_ERR_PRES="Во время выполнения были ошибки!"
LANG_SEND_EMAIL="Отправляем письмо с ошибками..."
LANG_NO_ERR="Ошибок нет! Все хорошо!"
LANG_STILL_SEND="Все равно отправляем письмо..."
LANG_EMAIL_FILES="Файлы и папки"
LANG_EMAIL_DBS="Базы данных"
LANG_EMAIL_DISABLE="Отправка почты отключена глобально!"
LANG_ERR_SUBJ="=?UTF-8?B?0J7RiNC40LHQutC4INGA0LXQt9C10YDQstC90L7Qs9C+INC60L7Qv9C40YDQvtCy0LDQvdC40Y8h?="
LANG_OK_SUBJ="=?UTF-8?B?0KPRgdC/0LXRiNC90L7QtSDRgNC10LfQtdGA0LLQvdC+0LUg0LrQvtC/0LjRgNC+0LLQsNC90LjQtSE=?="
LANG_ALL_DONE="Все сделано! Выходим..."

Примеры

:!: Для работы скрипта должно быть настроено не менее одного источника!

files.txt

files.txt
/opt/backupscr/backup.sh
/opt/backupscr/assets/
/opt/backupscr/sources/
 
/etc/cron.d/server_backup

mysqldbs.txt

mysqldbs.txt
database1
database2

Узнать названия баз можно выполнив команду:

mysql -u root -p"PaSSwoRD" -e "SHOW DATABASES;"

sqlitedbs.txt

sqlitedbs.txt
/path/to/base1/db.sqlite3
/path/to/base2/db2.sqlite3

Дисклеймер

  • Использование материалов данной базы знаний разрешено на условиях лицензии, указанной внизу каждой страницы! При использовании материалов активная гиперссылка на соответствующую страницу данной базы знаний обязательна!
  • Автор не несет и не может нести какую либо ответственность за последствия использования материалов, размещенных в данной базе знаний. Все материалы предоставляются по принципу «как есть». Используйте их исключительно на свой страх и риск.
  • Все высказывания, мысли или идеи автора, размещенные в материалах данной базе знаний, являются исключительно его личным субъективным мнением и могут не совпадать с мнением читателей!
  • При размещении ссылок в данной базе знаний на интернет-страницы третьих лиц автор не несет ответственности за их техническую функциональность (особенно отсутствие вирусов) и содержание! При обнаружении таких ссылок, можно и желательно сообщить о них в комментариях к соответствующей статье.
1)
Ну, или не очень «быдло»… не мне судить. LOL
2)
Пунктом 4 подразумевается запуск ровно раз в сутки, ни больше ни меньше!
3)
В любом составе, т.е. можно резервировать только MySQL или только файлы, а можно и все подряд.
4)
Другими словами, будет 7 копий за последние 7 дней и 12 копий за последние 12 месяцев по копии на месяц.
5)
Для использования FTP, необходимо незначительно подправить код скрипта.
6)
Из пакета MariaDB 10.5.19.
7)
Из пакета Mailutils.

Обсуждение

b3rsik0, 2023/12/20 17:41
Ошибка в строке: mysqldump $i -u ${MYSQL_USER} -p="${MYSQL_PASSWORD}" > ${DESTINATION_PATH}/temp/$i.sql

Нужно либо: -p"${MYSQL_PASSWORD}" или --password="${MYSQL_PASSWORD}"
Николай Солошин, 2023/12/20 18:20
Возможно, это зависит от контекста, ибо в моем случае, это работает... Вообще, начинаю вспоминать, что какая-то байда в этом месте в свое время была и у меня заработал именно этот вариант написания... но не возьмусь утверждать, давно это было. )))
b3rsik0, 2023/12/23 10:41
Еще желательно добавить список игнорирования папок и файлов.
Будет полезно для проектов где например присутствует папка vendor (symfony, nodejs, ruby и т.д.)

Например
IGNORE_LIST="ignfiles.txt"

if [ -s ${SOURCES_PATH}/${SOURCES_LIST} ]; then
echo "`date +%FT%H:%M:%S%:z` - ${LANG_COPY_FILES}" | tee -a ${LOG_PATH}.log
for i in "$(cat ${SOURCES_PATH}/${SOURCES_LIST})"; do
cp -rv --parents $i ${DESTINATION_PATH}/temp >> ${LOG_PATH}.log
done
if [ -s ${SOURCES_PATH}/${IGNORE_LIST} ]; then
echo "`date +%FT%H:%M:%S%:z` - ${LANG_DEL_FILES}" | tee -a ${LOG_PATH}.log
for i in "$(cat ${SOURCES_PATH}/${IGNORE_LIST})"; do
rm -r ${DESTINATION_PATH}/temp/$i >> ${LOG_PATH}.log
done
fi
else
echo "`date +%FT%H:%M:%S%:z` - ${LANG_THE_FILE} \"${SOURCES_LIST}\" ${LANG_NOT_PRES} ${LANG_SKIP_FILES}" | tee -a ${LOG_PATH}.log
(( NOTHING_TO_DO++ ))
LOG_COMPOSITION[0]=0
fi
Ваш комментарий:
E O P᠎ V O T A Y N᠎ W F D W P L K
 
Последнее изменение: 2023/05/13 13:04 — Николай Солошин