После установки Vaultwarden на Raspberry Pi 4 Model B с Raspberry Pi OS 11 (bullseye) остро встала задача надежного резервирования, в первую очередь, базы данных и, заодно, всяких разных файлов конфигураций. Немного поискав готовые решения, аля «галочки поставил и забыл», расстроился и понял, что это нам не Винда и, или искать готовый скрипт, или писать его самому…
Искать готовый, рабочий и выполняющий то, что нужно именно мне без размахивания напильником, это еще та задача, поэтому было решено немного попрактиковаться в быдлокодинге1) и сделать, как обычно, все самому.
Скрипт проверен на Raspberry Pi OS 11 (bullseye) x64 с командами следующих версий:
Если предполагается использование уведомлений по электронной почте, предварительно необходимо настроить клиент ssmtp!
sudo -s
apt update && apt install sqlite3 p7zip-full curl
mkdir -pv /opt/backupscr/assets/ /opt/backupscr/sources/ && chmod -Rv 750 /opt/backupscr/
nano /opt/backupscr/backup.sh
chmod +x /opt/backupscr/backup.sh
nano /opt/backupscr/assets/settings.txt
nano /opt/backupscr/assets/translation.txt
nano /opt/backupscr/sources/files.txt
nano /opt/backupscr/sources/mysqldbs.txt
nano /opt/backupscr/sources/sqlitedbs.txt
echo "10 3 * * * root cd /opt/backupscr/ && ./backup.sh" | tee /etc/cron.d/server_backup
Для запуска по требованию необходимо выполнить следующую команду:
cd /opt/backupscr/ && ./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.
# Служебные директории 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"
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="Все сделано! Выходим..."
Для работы скрипта должно быть настроено не менее одного источника!
/opt/backupscr/backup.sh /opt/backupscr/assets/ /opt/backupscr/sources/ /etc/cron.d/server_backup
database1 database2
Узнать названия баз можно выполнив команду:
mysql -u root -p"PaSSwoRD" -e "SHOW DATABASES;"
/path/to/base1/db.sqlite3 /path/to/base2/db2.sqlite3
Обсуждение
Нужно либо: -p"${MYSQL_PASSWORD}" или --password="${MYSQL_PASSWORD}"
Будет полезно для проектов где например присутствует папка 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
mkdir -p /opt/backup
nano /opt/backup/backup.sh
#!/bin/bash
dirs="/home /etc"
out="/opt/backup"
day=$(date +%A-%F)
hostname=$(hostname -s)
archive="$hostname-$day.tgz"
echo "### Directory backup has been started ###"
echo "### Creating backup archive ###"
tar czf $out/$archive $dirs
echo "### Backup successfully completed ###"
date +%A-%F-%T
ls -lh $out
chmod +x /opt/backup/backup.sh
mkdir -p /opt/backup
nano /opt/backup/backup.sh
#!/bin/bash
dirs="/home /etc"
out="/opt/backup"
day=$(date +%A-%F)
hostname=$(hostname -s)
archive="$hostname-$day.tgz"
echo "### Directory backup has been started ###"
echo "### Creating backup archive ###"
tar czf $out/$archive $dirs
echo "### Backup successfully completed ###"
date +%A-%F-%T
ls -lh $out
chmod +x /opt/backup/backup.sh
bash /opt/backup/backup.sh