feat: Add variable name_suffix; Pass shellcheck; Discard useless variables from main script
This commit is contained in:
parent
3d514678e7
commit
aaa7a5c2d3
6
Makefile
6
Makefile
@ -23,8 +23,10 @@ tests:
|
|||||||
for test in $(tests_dir)/*.bats; do bats --verbose-run --print-output-on-failure "$$test"; done
|
for test in $(tests_dir)/*.bats; do bats --verbose-run --print-output-on-failure "$$test"; done
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
shellcheck $(src_dir)/boring-backup || true
|
shellcheck $(src_dir)/boring-backup
|
||||||
shellcheck $(src_dir)/lib/* || true
|
shellcheck $(src_dir)/lib/*.sh
|
||||||
|
shellcheck $(src_dir)/lib/handlers/sources/*.sh
|
||||||
|
shellcheck $(src_dir)/lib/handlers/targets/*.sh
|
||||||
|
|
||||||
docs: builddir
|
docs: builddir
|
||||||
# See rst2man(1), rst2html(1), https://docutils.sourceforge.io/docs/index.html
|
# See rst2man(1), rst2html(1), https://docutils.sourceforge.io/docs/index.html
|
||||||
|
@ -20,7 +20,7 @@ backup files and databases.
|
|||||||
Описание
|
Описание
|
||||||
--------
|
--------
|
||||||
|
|
||||||
boring-backup - это расширяемая утилита для резервного копирования на основе сценариев Bash. По умолчанию предусмотрено создание резервных копий файлов с помощью ``tar`` и дампов баз данных MySQL/MariaDB и PostgreSQL.
|
boring-backup - это расширяемая утилита для резервного копирования на основе сценариев Bash.
|
||||||
|
|
||||||
Опции
|
Опции
|
||||||
-----
|
-----
|
||||||
@ -35,28 +35,26 @@ boring-backup - это расширяемая утилита для резерв
|
|||||||
Быстрый старт
|
Быстрый старт
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
boring-backup можно рассматривать как небольшой фреймворк/библиотеку для создания сценариев резервного копирования. boring-backup не реализует собственный язык сценариев, а полагается на Bash. В любом варианте применения утилита требует написания сценария для выполнения бэкапа.
|
boring-backup можно рассматривать как фреймворк или библиотеку для создания сценариев резервного копирования. Сценарии должны содержать валидный код на Bash. Сценарий импортируется в основной скрипт с помощью команды ``source`` (см. ``bash``\(1) п. SHELL BUILTIN COMMANDS).
|
||||||
|
|
||||||
В сценариях можно использовать переменные и функции. Сценарий импортируется в основной скрипт с помощью команды ``source`` (см. ``bash``\(1) п. SHELL BUILTIN COMMANDS).
|
|
||||||
|
|
||||||
Простейший сценарий резервного копирования выглядит следующим образом::
|
Простейший сценарий резервного копирования выглядит следующим образом::
|
||||||
|
|
||||||
sources=('file:/home/user')
|
sources=('file:/home/user')
|
||||||
targets=('file:/var/backup')
|
targets=('file:/var/backup')
|
||||||
|
|
||||||
Здесь массивы `sources` и `targets` определяют точки или поинты (`points`) резервного копирования — источники (`sources`) и назначения (`targets`). Это основные сущности, с которыми работает boring-backup. Вот некоторые особенности поинтов:
|
Здесь массивы `sources` и `targets` определяют точки, они же поинты (`points`) резервного копирования: источники (`sources`) и назначения (`targets`). Это основные сущности, с которыми работает boring-backup. Вот некоторые особенности поинтов:
|
||||||
|
|
||||||
- Все поинты указываются в формате URI. Описание URI также есть в этой документации ниже.
|
- Все поинты указываются в формате URI.
|
||||||
- Поинты могут указывать как на локальные (размещённые на текущей машине), так и на удалённые (размещённые на удалённой машине) ресурсы.
|
- Поинты могут указывать как на локальные (размещённые на текущей машине), так и на удалённые (размещённые на удалённой машине) ресурсы.
|
||||||
- Можно указать как несколько источников, так и несколько назначений.
|
- Можно указать как несколько источников, так и несколько назначений.
|
||||||
|
|
||||||
Для выполнения бэкапа директорий или отдельных файлов применяется схема URI `file`. В схеме `file` нельзя указать директорию, размещённую на удалённом хранилище (за исключением случая, когда удалённое хранилище примонтировано как файловая система), для этого используйте другие схемы. В примере выше будет выполнена резервная копия локальной директории /home/user в другую локальную директорию /var/backup. Поскольку в сценарии из примера нет ничего, кроме указания точек `sources` и `targets`, бэкап будет выполнен с параметрами по умолчанию: директория /home/user будет заархивирована с помощью утилиты ``tar``\(1), архив будет сжат с помощью утилиты ``gzip``\(1) и помещён в директорию /var/backup. Имя архива будет сложено из строки::
|
Для выполнения бэкапа директорий или отдельных файлов применяется схема URI `file`. В схеме `file` нельзя указать директорию, размещённую на удалённом хранилище (за исключением случая, когда удалённое хранилище примонтировано как файловая система), для этого используйте другие схемы. В примере выше будет выполнена резервная копия локальной директории /home/user в другую локальную директорию /var/backup. Поскольку в сценарии из примера нет ничего, кроме указания точек `sources` и `targets`, бэкап будет выполнен с параметрами по умолчанию: директория /home/user будет заархивирована с помощью утилиты ``tar``\(1) и помещён в директорию /var/backup. Имя архива будет сложено из строки::
|
||||||
|
|
||||||
${prefix}${name}${date_fmt}${name_ext}
|
${name_prefix}${name}${name_date_fmt}${name_suffix}${name_ext}
|
||||||
|
|
||||||
Описание переменных см. в разделе ПЕРЕМЕННЫЕ. Пример имени файла::
|
Описание переменных см. в разделе ПЕРЕМЕННЫЕ. Пример имени файла::
|
||||||
|
|
||||||
example.sh_example_20220515-0953.tar.gz
|
example.sh_example_2022.05.15-0953.tar
|
||||||
|
|
||||||
Обзор URI
|
Обзор URI
|
||||||
---------
|
---------
|
||||||
@ -101,8 +99,9 @@ Perl 5::
|
|||||||
``````````````````````````````````````````````````
|
``````````````````````````````````````````````````
|
||||||
|
|
||||||
file
|
file
|
||||||
Может содержать путь к локальному файлу или директории. Файлы архивируются при помощи ``tar`` и по умолчанию сжимаются с помощью ``gzip``. Примеры::
|
Может содержать путь к локальному файлу или директории. Файлы архивируются при помощи ``tar`` и по умолчанию не сжимаются. Cжатие архива может быть включено через переменную `compression`. Примеры::
|
||||||
|
|
||||||
|
/var/www/www-root/data
|
||||||
file:/var/www/html
|
file:/var/www/html
|
||||||
file:///home/jhon/cool_stuff.txt
|
file:///home/jhon/cool_stuff.txt
|
||||||
|
|
||||||
@ -115,17 +114,17 @@ mysql
|
|||||||
|
|
||||||
mysql://[username[:password]@]hostname[:port]/database
|
mysql://[username[:password]@]hostname[:port]/database
|
||||||
|
|
||||||
С помощью утилиты ``mysqldump``\(1) формируется дамп в формате SQL, файл по умолчанию сжимается утилитой ``gzip``.
|
С помощью утилиты ``mysqldump``\(1) формируется дамп в формате SQL. Файл по умолчанию не сжимается, но сжатие может быть включено через переменную `compression`.
|
||||||
|
|
||||||
См. также ``handler::mysqldump``
|
См. также ``handler::mysqldump``
|
||||||
|
|
||||||
postgres
|
postgres
|
||||||
Аналогично `mysql`, но для PostgreSQL. Для создания дампа используется ``pg_dump``\(1), файлы по умолчанию сохраняются с расширением .psql.gz.
|
Аналогично `mysql`, но для PostgreSQL. Для создания дампа используется ``pg_dump``\(1) с расширением .psql. Файл по умолчанию не сжимается, но сжатие может быть включено через переменную `compression`.
|
||||||
|
|
||||||
См. также ``handler::pg_dump``
|
См. также ``handler::pg_dump``
|
||||||
|
|
||||||
sqlite
|
sqlite
|
||||||
Схема для баз данных SQLite. Ничем не отличается от `file`, добавлена для наглядного обозначения, что источником является БД SQLite, а не иной файл.
|
Схема для баз данных SQLite. Мало отличается от `file`, добавлена для наглядного обозначения, что источником является БД SQLite, а не иной файл.
|
||||||
|
|
||||||
См. также ``handler::sqlite``
|
См. также ``handler::sqlite``
|
||||||
|
|
||||||
@ -133,7 +132,7 @@ sqlite
|
|||||||
``````````````````````````````````````````````````
|
``````````````````````````````````````````````````
|
||||||
|
|
||||||
file
|
file
|
||||||
В контексте `targets` указывает директорию для сохранения бэкапов. В массиве `targets` обязательно должен быть хотя бы один поинт со схемой `file`. Этот поинт будет использован как основной и сохранён в переменные ``__main_target`` (содержит URI) и ``__main_target_path`` (path). Если в массиве присутствует несколько поинтов со схемой `file`, то в качестве основного будет выбран первый по порядку поинт. Все бэкапы первоначально будут сохраняться в директорию ``__main_target_path`` и затем копироваться в другой таргет с помощью соответствующего обработчика. В случае копировани из одной директории в другую используется утилита ``cp``\(1).
|
В контексте `targets` указывает директорию для сохранения бэкапов. В массиве `targets` обязательно должен быть хотя бы один поинт со схемой `file`. Этот поинт будет использован как основной и сохранён в переменные ``__main_target`` (содержит URI) и ``__main_target_path`` (содержит компонент URI path). Если в массиве присутствует несколько поинтов со схемой `file`, то в качестве основного будет выбран первый по порядку поинт. Все бэкапы первоначально будут сохраняться в директорию ``__main_target_path`` и затем копироваться в другой таргет с помощью соответствующего обработчика. В случае копировани из одной директории в другую используется утилита ``cp``\(1).
|
||||||
|
|
||||||
Избегайте указания в одном сценарии таргетов, которые ведут одновременно на примонтированный к локальной машине сетевой диск и другое удалённое хранилище. В таком случае архивы будут создаваться на сетевом диске и далее снова копироваться по сети, что будет очень медленно.
|
Избегайте указания в одном сценарии таргетов, которые ведут одновременно на примонтированный к локальной машине сетевой диск и другое удалённое хранилище. В таком случае архивы будут создаваться на сетевом диске и далее снова копироваться по сети, что будет очень медленно.
|
||||||
|
|
||||||
@ -165,11 +164,11 @@ dav, davs
|
|||||||
|
|
||||||
boring-backup предполагает, что для всех резервных копий необходимо создавать архивы. Поэтому вам нужно следить за тем, чтобы в локальном хранилище всегда хватало дискового пространства для создания новых архивов. boring-backup также не удаляет старые архивы и вам также надо позаботиться об удалении устаревших резервных копий. Изменить это поведение можно с помощью пользовательских функций в сценариях. В этом разделе речь пойдёт о поведении, которое установлено по умолчанию. См. функции ``builtin_backup``, ``process_sources``, ``process_targets``.
|
boring-backup предполагает, что для всех резервных копий необходимо создавать архивы. Поэтому вам нужно следить за тем, чтобы в локальном хранилище всегда хватало дискового пространства для создания новых архивов. boring-backup также не удаляет старые архивы и вам также надо позаботиться об удалении устаревших резервных копий. Изменить это поведение можно с помощью пользовательских функций в сценариях. В этом разделе речь пойдёт о поведении, которое установлено по умолчанию. См. функции ``builtin_backup``, ``process_sources``, ``process_targets``.
|
||||||
|
|
||||||
Базовая концепция строится на том, что существует несколько точек источников и несколько точек назначения. Создаётся резервная копия источника и копируется в точку назначения.
|
Базовая концепция строится на том, что существует несколько источников и несколько точек назначения. Создаётся резервная копия источника и копируется в точку назначения.
|
||||||
|
|
||||||
Для всех источников в сценарии выполняется локальная резервная копия. После этого локальная копия переносится в удалённые точки назначения, если они заданы. Копирование локального бэкапа будет осуществлёно столько раз, сколько точек назначения задано. В каждом сценарии обязательно должен быть задан хотя бы один источник и одна точка назначения. При этом в списке точек назначения обязательно должна присутствовать точка со схемой `file`. Именно в неё будут сохранены архивы.
|
Для всех источников в сценарии выполняется локальная резервная копия. После этого локальная копия переносится в удалённые точки назначения, если они заданы. Копирование локального бэкапа будет осуществлёно столько раз, сколько точек назначения задано. В каждом сценарии обязательно должен быть задан хотя бы один источник и одна точка назначения. При этом в списке точек назначения обязательно должна присутствовать точка со схемой `file`. Именно в неё будут сохранены архивы.
|
||||||
|
|
||||||
Пример. Имеется следующий сценарий::
|
Например, имеется следующий сценарий::
|
||||||
|
|
||||||
sources=(
|
sources=(
|
||||||
'mysql://wordpress:1jobrRRjtLYs@localhost/wordpress'
|
'mysql://wordpress:1jobrRRjtLYs@localhost/wordpress'
|
||||||
@ -212,80 +211,89 @@ WebDAV
|
|||||||
``backups``
|
``backups``
|
||||||
Массив со списком созданных бэкапов. Содержит абсолютные пути к файлам. Заполняется функциями-обработчиками `sources`.
|
Массив со списком созданных бэкапов. Содержит абсолютные пути к файлам. Заполняется функциями-обработчиками `sources`.
|
||||||
|
|
||||||
Тип: массив
|
| Тип: массив
|
||||||
Умолчание: ()
|
| Умолчание: ()
|
||||||
|
|
||||||
``errors``
|
``errors``
|
||||||
Массив с текстами ошибок. Заполняется функцией ``err`` с опцией ``-a``.
|
Массив с текстами ошибок. Заполняется функцией ``err`` с опцией ``-a``.
|
||||||
|
|
||||||
Тип: массив
|
| Тип: массив
|
||||||
Умолчание: ()
|
| Умолчание: ()
|
||||||
|
|
||||||
``log_date_fmt``
|
``log_date_fmt``
|
||||||
Формат даты в логе. См. ``date``\(1).
|
Формат даты в логе. См. ``date``\(1).
|
||||||
|
|
||||||
Тип: строка
|
| Тип: строка
|
||||||
Умолчание: %d/%b/%Y:%H:%M:%S %z
|
| Умолчание: %d/%b/%Y:%H:%M:%S %z
|
||||||
|
|
||||||
``name_prefix``
|
``name_prefix``
|
||||||
Префикс имени файла. Может быть задан в скрипте.
|
Префикс имени файла. Может быть задан в скрипте.
|
||||||
|
|
||||||
Тип: строка
|
| Тип: строка
|
||||||
Умолчание: имя_скрипта\_
|
| Умолчание: имя_скрипта\_
|
||||||
|
|
||||||
``name``
|
``name``
|
||||||
Соответствует имени архивируемой директории/файла полученного из `path` с помощью утилиты ``basename``\(1).
|
Соответствует имени архивируемой директории/файла полученного из `path` с помощью утилиты ``basename``\(1).
|
||||||
|
|
||||||
Тип: строка
|
| Тип: строка
|
||||||
Умолчание: нет
|
| Умолчание: нет
|
||||||
|
|
||||||
``name_date_fmt``
|
``name_date_fmt``
|
||||||
Дата, интерпретируемая утилитой ``date``\(1).
|
Дата, интерпретируемая утилитой ``date``\(1).
|
||||||
|
|
||||||
Тип: строка
|
| Тип: строка
|
||||||
Умолчание: _%Y%m%d-%H%M
|
| Умолчание: _%Y%m%d
|
||||||
|
|
||||||
|
``name_suffix``
|
||||||
|
Суффикс, добавляемый к имени файла перед расширением. Строка интерпретируется утилитой ``date``.
|
||||||
|
|
||||||
|
| Тип: строка
|
||||||
|
| Умолчание: -%H%M
|
||||||
|
|
||||||
``name_ext``
|
``name_ext``
|
||||||
Расширение имени файла, соответствующее формату архива.
|
Расширение имени файла, соответствующее формату архива.
|
||||||
|
|
||||||
Тип: строка
|
| Тип: строка
|
||||||
Умолчание: .tar.gz
|
| Умолчание: .tar.gz
|
||||||
|
|
||||||
``tar_options``
|
``tar_options``
|
||||||
Опции ``tar``. См. ``tar``\(1).
|
Опции ``tar``. См. ``tar``\(1).
|
||||||
|
|
||||||
Тип: строка
|
| Тип: строка
|
||||||
Умолчание: -acf
|
| Умолчание: -acf
|
||||||
|
|
||||||
``tar_exclude``
|
``tar_exclude``
|
||||||
Список имён для опции ``tar`` ``--exclude``.
|
Список имён для опции ``tar`` ``--exclude``.
|
||||||
|
|
||||||
Тип: массив
|
| Тип: массив
|
||||||
Умолчание: нет
|
| Умолчание: нет
|
||||||
|
|
||||||
``compression``
|
``compression``
|
||||||
В контексте ``tar`` работа этой фичи базируется на опции ``tar`` ``--auto-compress``. Переменная может принимать значения, в соответствии с табилцей ниже. Если переменная имеет значение, отличное от перечисленныых, то будет использован ``gzip``.
|
В контексте ``tar`` работа этой фичи базируется на опции ``tar`` ``--auto-compress``. Переменная может принимать значения, в соответствии с табилцей ниже. Если переменная имеет значение, отличное от перечисленныых, то будет использован ``gzip``. Если переменная пуста или не задана, то сжатие будет отключено.
|
||||||
|
|
||||||
|
| Тип: строка
|
||||||
|
| Умолчание: нет
|
||||||
|
|
||||||
======================== ================ ========
|
==================== ================ ========
|
||||||
Значение tar_compression Расширение файла Утилита
|
Значение compression Расширение файла Утилита
|
||||||
======================== ================ ========
|
==================== ================ ========
|
||||||
gzip, gz .tar.gz gzip
|
gzip, gz .tar.gz gzip
|
||||||
tgz .tgz gzip
|
tgz .tgz gzip
|
||||||
taz .taz gzip
|
taz .taz gzip
|
||||||
compress, Z .tar.Z compress
|
compress, Z .tar.Z compress
|
||||||
taZ .taZ compress
|
taZ .taZ compress
|
||||||
bzip2, bz2 .tar.bz2 bzip2
|
bzip2, bz2 .tar.bz2 bzip2
|
||||||
tz2 .tz2 bzip2
|
tz2 .tz2 bzip2
|
||||||
tbz2 .tbz2 bzip2
|
tbz2 .tbz2 bzip2
|
||||||
tbz .tbz bzip2
|
tbz .tbz bzip2
|
||||||
lzip, lz .tar.lz lzip
|
lzip, lz .tar.lz lzip
|
||||||
lzma .tar.lzma lzma
|
lzma .tar.lzma lzma
|
||||||
tlz .tlz lzma
|
tlz .tlz lzma
|
||||||
lzop, lzo .tar.lzo lzop
|
lzop, lzo .tar.lzo lzop
|
||||||
xz .tar.xz xz
|
xz .tar.xz xz
|
||||||
zstd, zst .tar.zst zstd
|
zstd, zst .tar.zst zstd
|
||||||
tzst .tzst zstd
|
tzst .tzst zstd
|
||||||
======================== ================ ========
|
==================== ================ ========
|
||||||
|
|
||||||
Функции
|
Функции
|
||||||
-------
|
-------
|
||||||
|
@ -20,21 +20,17 @@ __version='0.0.0'
|
|||||||
__config=
|
__config=
|
||||||
__verbose=
|
__verbose=
|
||||||
__log_file='./log.txt'
|
__log_file='./log.txt'
|
||||||
__log_date_fmt='%d/%b/%Y:%H:%M:%S %z'
|
|
||||||
__pid_file='/tmp/boring-backup.pid'
|
__pid_file='/tmp/boring-backup.pid'
|
||||||
__tar_options='-acf'
|
|
||||||
__tar_exclude=
|
|
||||||
__name_date_fmt='_%Y%m%d-%H%M'
|
|
||||||
__compression=
|
|
||||||
__errors_count=0
|
__errors_count=0
|
||||||
__errors_file='/tmp/boring-backup.errors'
|
__errors_file='/tmp/boring-backup.errors'
|
||||||
|
|
||||||
echo $__errors_count > $__errors_file
|
echo "$__errors_count" > "$__errors_file"
|
||||||
|
|
||||||
LIBRARY="${LIBRARY:-./lib}"
|
LIBRARY="${LIBRARY:-./lib}"
|
||||||
|
|
||||||
# Source library
|
# Source library
|
||||||
if [ -f "$LIBRARY/lib.sh" ]; then
|
if [ -f "$LIBRARY/lib.sh" ]; then
|
||||||
|
# shellcheck disable=SC1091
|
||||||
. "$LIBRARY/lib.sh"
|
. "$LIBRARY/lib.sh"
|
||||||
else
|
else
|
||||||
echo "Error: Cannot source library from $LIBRARY" >&2
|
echo "Error: Cannot source library from $LIBRARY" >&2
|
||||||
@ -69,7 +65,7 @@ optval() {
|
|||||||
#
|
#
|
||||||
# Parse --opt VAL and --opt=VAL options.
|
# Parse --opt VAL and --opt=VAL options.
|
||||||
# Requires 2 arguments: $1, $2.
|
# Requires 2 arguments: $1, $2.
|
||||||
# Return variables:
|
# Set variables:
|
||||||
# opt option name.
|
# opt option name.
|
||||||
# val option value.
|
# val option value.
|
||||||
# sft value for shift.
|
# sft value for shift.
|
||||||
@ -96,7 +92,8 @@ for args in "$@"; do
|
|||||||
set -- "$@" "$args";; # save long options
|
set -- "$@" "$args";; # save long options
|
||||||
-*)
|
-*)
|
||||||
args="$(echo "${args:1}" | grep -o . | xargs -I {} echo -n '-{} ')"
|
args="$(echo "${args:1}" | grep -o . | xargs -I {} echo -n '-{} ')"
|
||||||
set -- "$@" $args;;
|
# shellcheck disable=SC2086
|
||||||
|
set -- "$@" $args;; # 'args' must be unquoted!
|
||||||
*)
|
*)
|
||||||
set -- "$@" "$args";; # save positional arguments
|
set -- "$@" "$args";; # save positional arguments
|
||||||
esac
|
esac
|
||||||
@ -123,20 +120,22 @@ done
|
|||||||
log "Backup STARTED"
|
log "Backup STARTED"
|
||||||
|
|
||||||
# Check PID file
|
# Check PID file
|
||||||
if [ -e $__pid_file ]; then
|
if [ -e "$__pid_file" ]; then
|
||||||
if [ -z "$(ps ax -o pid | grep "$(cat $__pid_file)")" ]; then
|
# shellcheck disable=SC2009
|
||||||
err "Process $(cat $__pid_file) died."
|
# shellcheck disable=SC2143
|
||||||
rm $__pid_file
|
if [ -z "$(ps ax -o pid | grep "$(cat "$__pid_file")")" ]; then
|
||||||
|
err "Process $(cat "$__pid_file") died."
|
||||||
|
rm "$__pid_file"
|
||||||
else
|
else
|
||||||
err -e "Process $(cat $__pid_file) still running.";
|
err -e "Process $(cat "$__pid_file") still running.";
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Touch PID file
|
# Touch PID file
|
||||||
echo $$ > $__pid_file
|
echo "$$" > "$__pid_file"
|
||||||
|
|
||||||
# Scripts counter.
|
# Scripts counter.
|
||||||
__count=${#__args[@]} # count
|
__count="${#__args[@]}" # count
|
||||||
__iter=1 # iterator
|
__iter=1 # iterator
|
||||||
|
|
||||||
# Startup log.
|
# Startup log.
|
||||||
@ -144,18 +143,22 @@ date +'Start: %d %b %Y %T %z'
|
|||||||
log -p "Library path: $LIBRARY"
|
log -p "Library path: $LIBRARY"
|
||||||
log -p "Log file: $__log_file"
|
log -p "Log file: $__log_file"
|
||||||
log -p "Configuration file: $([ "$__config" ] || echo not specified && echo "$__config")"
|
log -p "Configuration file: $([ "$__config" ] || echo not specified && echo "$__config")"
|
||||||
log "Scripts to process (${__count}): ${__args[@]}"
|
log "Scripts to process (${__count}): ${__args[*]}"
|
||||||
|
|
||||||
for script in "${__args[@]}"; do
|
for script in "${__args[@]}"; do
|
||||||
# Initialise variables
|
# Initialise variables
|
||||||
__user_script="$script"
|
__user_script="$script"
|
||||||
|
|
||||||
|
# shellcheck disable=SC2034
|
||||||
backups=() # Array of created backups, contains full pathes
|
backups=() # Array of created backups, contains full pathes
|
||||||
|
# shellcheck disable=SC2034
|
||||||
errors=() # Array of error messages written by err() function
|
errors=() # Array of error messages written by err() function
|
||||||
|
|
||||||
# Source scripts
|
# Source scripts
|
||||||
source_script "$script"
|
source_script "$script"
|
||||||
|
|
||||||
# Config can ovewrite script functions and variables
|
# Config can ovewrite script functions and variables
|
||||||
|
# shellcheck source=/dev/null
|
||||||
[ -n "$__config" ] && . "$__config"
|
[ -n "$__config" ] && . "$__config"
|
||||||
|
|
||||||
echo
|
echo
|
||||||
@ -209,7 +212,7 @@ if [[ "$(cat $__errors_file)" != '0' ]]; then
|
|||||||
echo
|
echo
|
||||||
log -p "Backup [Failed]: Process finished with $(cat $__errors_file) errors."\
|
log -p "Backup [Failed]: Process finished with $(cat $__errors_file) errors."\
|
||||||
"See $__log_file for info."
|
"See $__log_file for info."
|
||||||
rm $__errors_file
|
rm "$__errors_file"
|
||||||
else
|
else
|
||||||
echo -e "\nBackup [Done]"
|
echo -e "\nBackup [Done]"
|
||||||
fi
|
fi
|
||||||
@ -217,4 +220,4 @@ fi
|
|||||||
log "Backup FINISHED"
|
log "Backup FINISHED"
|
||||||
|
|
||||||
# Remove PID file
|
# Remove PID file
|
||||||
rm $__pid_file
|
rm "$__pid_file"
|
||||||
|
@ -37,7 +37,9 @@ process_source() {
|
|||||||
mysql) handler='handler::mysqldump';;
|
mysql) handler='handler::mysqldump';;
|
||||||
postgres) handler='handler::pg_dump';;
|
postgres) handler='handler::pg_dump';;
|
||||||
sqlite) handler='handler::sqlite';;
|
sqlite) handler='handler::sqlite';;
|
||||||
*) echo "Error: $__user_script: Unsupported URI scheme: $scheme" >&2; exit 1;;
|
*) # shellcheck disable=SC2154
|
||||||
|
# '__user_script' is assigned in main script.
|
||||||
|
echo "Error: $__user_script: Unsupported URI scheme: $scheme" >&2; exit 1;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# Run handler function
|
# Run handler function
|
||||||
@ -84,10 +86,12 @@ builtin_backup() {
|
|||||||
#
|
#
|
||||||
# Usage: builtin_backup
|
# Usage: builtin_backup
|
||||||
|
|
||||||
|
# shellcheck disable=SC2154
|
||||||
for source in "${sources[@]}"; do
|
for source in "${sources[@]}"; do
|
||||||
process_source "$source"
|
process_source "$source"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# shellcheck disable=SC2154
|
||||||
for target in "${targets[@]}"; do
|
for target in "${targets[@]}"; do
|
||||||
process_target "$target"
|
process_target "$target"
|
||||||
done
|
done
|
||||||
@ -102,21 +106,15 @@ gen_backup_name() {
|
|||||||
#
|
#
|
||||||
# Usage: gen_backup_name NAME_EXT
|
# Usage: gen_backup_name NAME_EXT
|
||||||
|
|
||||||
local prefix
|
|
||||||
local name
|
local name
|
||||||
local date_fmt
|
|
||||||
local name_ext
|
local name_ext
|
||||||
|
|
||||||
[ -n "$name_prefix" ] || { prefix="${__user_script}_"; }
|
name_prefix="${name_prefix:-${__user_script}_}"
|
||||||
name="$(basename $path)" # 'path' is variable parsed from URI
|
# shellcheck disable=SC2154
|
||||||
|
name="$(basename "$path")" # 'path' is variable parsed from URI
|
||||||
name_ext="$1"
|
name_ext="$1"
|
||||||
|
name_date_fmt="${name_date_fmt:-_%Y.%m.%d}"
|
||||||
|
name_suffix="${name_suffix:--%H%M}"
|
||||||
|
|
||||||
# Overwrite __name_date_fmt
|
date +"${name_prefix}${name}${name_date_fmt}${name_suffix}${name_ext}"
|
||||||
if [ -n "$name_date_fmt" ]; then
|
|
||||||
date_fmt="$name_date_fmt"
|
|
||||||
else
|
|
||||||
date_fmt="$__name_date_fmt"
|
|
||||||
fi
|
|
||||||
|
|
||||||
date +"${prefix}${name}${date_fmt}${name_ext}"
|
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ log() {
|
|||||||
[[ ! -t 0 ]] || [[ "$#" == 0 ]] && message="$(cat <&0)"
|
[[ ! -t 0 ]] || [[ "$#" == 0 ]] && message="$(cat <&0)"
|
||||||
|
|
||||||
# Set log date format
|
# Set log date format
|
||||||
[ "$log_date_fmt" ] || log_date_fmt="$__log_date_fmt"
|
log_date_fmt="${log_date_fmt:-%d/%b/%Y:%H:%M:%S %z}"
|
||||||
|
|
||||||
while (( "$#" )); do
|
while (( "$#" )); do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
@ -44,7 +44,8 @@ log() {
|
|||||||
[[ "$print" == 1 ]] && echo -e "$message"
|
[[ "$print" == 1 ]] && echo -e "$message"
|
||||||
|
|
||||||
while read -r line; do
|
while read -r line; do
|
||||||
if [[ "$line" ]]; then
|
if [ -n "$line" ]; then
|
||||||
|
# shellcheck disable=SC2154
|
||||||
printf '[%s] %s\n' "$(date +"$log_date_fmt")" "$line" >> "$__log_file"
|
printf '[%s] %s\n' "$(date +"$log_date_fmt")" "$line" >> "$__log_file"
|
||||||
fi
|
fi
|
||||||
done <<< "$(sed -r 's/\x1B\[(([0-9]+)(;[0-9]+)*)?[m,K,H,f,J]//g' <<< "$message")"
|
done <<< "$(sed -r 's/\x1B\[(([0-9]+)(;[0-9]+)*)?[m,K,H,f,J]//g' <<< "$message")"
|
||||||
@ -69,7 +70,8 @@ err() {
|
|||||||
case "$args" in
|
case "$args" in
|
||||||
-*)
|
-*)
|
||||||
args="$(echo "${args:1}" | grep -o . | xargs -I {} echo -n '-{} ')"
|
args="$(echo "${args:1}" | grep -o . | xargs -I {} echo -n '-{} ')"
|
||||||
set -- "$@" $args;;
|
# shellcheck disable=SC2086
|
||||||
|
set -- "$@" $args;; # 'args' must be unquoted!
|
||||||
*)
|
*)
|
||||||
set -- "$@" "$args";; # save positional arguments
|
set -- "$@" "$args";; # save positional arguments
|
||||||
esac
|
esac
|
||||||
@ -100,9 +102,10 @@ err() {
|
|||||||
# Exit
|
# Exit
|
||||||
if [ "$errexit" ]; then
|
if [ "$errexit" ]; then
|
||||||
# Count errors for backup recap
|
# Count errors for backup recap
|
||||||
__errors_count="$(cat $__errors_file)"
|
# shellcheck disable=SC2154
|
||||||
|
__errors_count="$(cat "$__errors_file")"
|
||||||
((__errors_count++)) || true
|
((__errors_count++)) || true
|
||||||
echo $__errors_count > "$__errors_file"
|
echo "$__errors_count" > "$__errors_file"
|
||||||
|
|
||||||
log -p "Backup ERROR: Exiting with previous errors" >&2; exit 1
|
log -p "Backup ERROR: Exiting with previous errors" >&2; exit 1
|
||||||
fi
|
fi
|
||||||
@ -113,7 +116,8 @@ try() {
|
|||||||
#
|
#
|
||||||
# Usage: try COMMAND
|
# Usage: try COMMAND
|
||||||
|
|
||||||
if eval "$@" 2>> "$__log_file"; then
|
# https://www.shellcheck.net/wiki/SC2294
|
||||||
|
if "$@" 2>> "$__log_file"; then
|
||||||
return 0 # Success
|
return 0 # Success
|
||||||
else
|
else
|
||||||
# Count errors
|
# Count errors
|
||||||
@ -168,6 +172,11 @@ set_compression() {
|
|||||||
xz) file_ext='.xz'; compr_util='xz';;
|
xz) file_ext='.xz'; compr_util='xz';;
|
||||||
zstd|zst) file_ext='.zst'; compr_util='zstd';;
|
zstd|zst) file_ext='.zst'; compr_util='zstd';;
|
||||||
tzst) file_ext='.zst'; compr_util='zstd';;
|
tzst) file_ext='.zst'; compr_util='zstd';;
|
||||||
*) file_ext=''; compr_util='';; # No compression
|
*) # No compression
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
file_ext=''
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
compr_util=''
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
@ -32,12 +32,14 @@ handler::mysqldump() {
|
|||||||
local file_ext
|
local file_ext
|
||||||
|
|
||||||
uri="$1"
|
uri="$1"
|
||||||
|
# shellcheck disable=SC2154
|
||||||
dst_path="$__main_target_path"
|
dst_path="$__main_target_path"
|
||||||
|
|
||||||
parse_uri "$uri"
|
parse_uri "$uri"
|
||||||
|
|
||||||
[ -z "$port" ] && port=3306 # Set default MySQL port.
|
[ -z "$port" ] && port=3306 # Set default MySQL port.
|
||||||
|
|
||||||
|
# shellcheck disable=SC2154
|
||||||
[ -n "$__verbose" ] && echo "Dumping database '${path##*/}'"\
|
[ -n "$__verbose" ] && echo "Dumping database '${path##*/}'"\
|
||||||
"owned by ${username}@${hostname} ..." | log -p
|
"owned by ${username}@${hostname} ..." | log -p
|
||||||
|
|
||||||
@ -61,7 +63,10 @@ handler::mysqldump() {
|
|||||||
compr_cmd="cat"
|
compr_cmd="cat"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
mysqldump_opts="${mysqldump_opts:-}"
|
||||||
|
|
||||||
# NOTE! mysqldump_opts and compr_cmd variables must be unquoted!
|
# NOTE! mysqldump_opts and compr_cmd variables must be unquoted!
|
||||||
|
# shellcheck disable=SC2086,SC2154
|
||||||
try mysqldump $mysqldump_opts \
|
try mysqldump $mysqldump_opts \
|
||||||
--host="$hostname" \
|
--host="$hostname" \
|
||||||
--port="$port" \
|
--port="$port" \
|
||||||
@ -76,33 +81,32 @@ handler::mysqldump() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# POSTGRESQL vvv
|
# POSTGRESQL
|
||||||
|
#bk_dump_postgresql() {
|
||||||
bk_dump_postgresql() {
|
# # Do PostgreSQL dump.
|
||||||
# Do PostgreSQL dump.
|
#
|
||||||
|
# bk_log "Dumping database '${path##*/}' owned by ${user}@${host} (PostgreSQL) ..."
|
||||||
bk_log "Dumping database '${path##*/}' owned by ${user}@${host} (PostgreSQL) ..."
|
#
|
||||||
|
# dump_name="$entry_local"/"$(bk_get_name "$db_name" .psql.gz)"
|
||||||
dump_name="$entry_local"/"$(bk_get_name "$db_name" .psql.gz)"
|
# [ "$port" ] || port=5432 # Set default PostgreSQL port.
|
||||||
[ "$port" ] || port=5432 # Set default PostgreSQL port.
|
# export PGPASSWORD="$password"
|
||||||
export PGPASSWORD="$password"
|
#
|
||||||
|
# pg_dump \
|
||||||
pg_dump \
|
# --host="$host" \
|
||||||
--host="$host" \
|
# --port="$port" \
|
||||||
--port="$port" \
|
# --dbname="${path##*/}" \
|
||||||
--dbname="${path##*/}" \
|
# --username="$user" \
|
||||||
--username="$user" \
|
# --no-password | gzip -c > "$dump_name" |& bk_log
|
||||||
--no-password | gzip -c > "$dump_name" |& bk_log
|
#
|
||||||
|
# unset PGPASSWORD
|
||||||
unset PGPASSWORD
|
#
|
||||||
|
# if [ -s "$dump_name" ]; then
|
||||||
if [ -s "$dump_name" ]; then
|
# bk_log "Dumping database '${path##*/}' owned by ${user}@${host} (PostgreSQL) [Done]"
|
||||||
bk_log "Dumping database '${path##*/}' owned by ${user}@${host} (PostgreSQL) [Done]"
|
# bk_log "Dump saved as: $dump_name"
|
||||||
bk_log "Dump saved as: $dump_name"
|
# bk_upload_file "$dump_name"
|
||||||
bk_upload_file "$dump_name"
|
#
|
||||||
|
# else
|
||||||
else
|
# rm "$dump_name"
|
||||||
rm "$dump_name"
|
# bk_err "Something went wrong. Dump size is 0 bytes. Removing $dump_name"
|
||||||
bk_err "Something went wrong. Dump size is 0 bytes. Removing $dump_name"
|
# fi
|
||||||
fi
|
#}
|
||||||
}
|
|
||||||
|
@ -26,16 +26,17 @@ handler::tar() {
|
|||||||
local uri
|
local uri
|
||||||
local src_path
|
local src_path
|
||||||
local dst_path
|
local dst_path
|
||||||
local opts
|
|
||||||
local archive
|
local archive
|
||||||
local exclude
|
local exclude
|
||||||
local file_ext
|
local file_ext
|
||||||
|
|
||||||
uri="$1"
|
uri="$1"
|
||||||
|
# shellcheck disable=SC2154
|
||||||
dst_path="$__main_target_path"
|
dst_path="$__main_target_path"
|
||||||
|
|
||||||
parse_uri "$uri"
|
parse_uri "$uri"
|
||||||
|
|
||||||
|
# shellcheck disable=SC2154
|
||||||
if [ -f "$path" ] || [ -d "$path" ]; then
|
if [ -f "$path" ] || [ -d "$path" ]; then
|
||||||
src_path="$path"
|
src_path="$path"
|
||||||
else
|
else
|
||||||
@ -51,15 +52,8 @@ handler::tar() {
|
|||||||
# Exit if tar is not installed
|
# Exit if tar is not installed
|
||||||
is_installed tar
|
is_installed tar
|
||||||
|
|
||||||
# Overwrire __tar_options
|
# Set tar_exclude
|
||||||
if [ -n "$tar_options" ]; then
|
if [ -n "$tar_exclude" ]; then
|
||||||
opts="$tar_options"
|
|
||||||
else
|
|
||||||
opts="$__tar_options"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Overwrite __tar_exclude
|
|
||||||
if [ "$tar_exclude" ]; then
|
|
||||||
for item in "${tar_exclude[@]}"; do
|
for item in "${tar_exclude[@]}"; do
|
||||||
exclude+=" --exclude $item"
|
exclude+=" --exclude $item"
|
||||||
done
|
done
|
||||||
@ -67,6 +61,9 @@ handler::tar() {
|
|||||||
exclude=
|
exclude=
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Set options
|
||||||
|
tar_options="${tar_options:--acf}"
|
||||||
|
|
||||||
# Select filename extension by compression type.
|
# Select filename extension by compression type.
|
||||||
if [ "$compression" ]; then
|
if [ "$compression" ]; then
|
||||||
# Make sure for the `--auto-compress` is enabled in __tar_options
|
# Make sure for the `--auto-compress` is enabled in __tar_options
|
||||||
@ -99,14 +96,14 @@ handler::tar() {
|
|||||||
[ "$__verbose" ] && {
|
[ "$__verbose" ] && {
|
||||||
echo "Source path: $src_path"
|
echo "Source path: $src_path"
|
||||||
echo "Destination path: $dst_path"
|
echo "Destination path: $dst_path"
|
||||||
echo "Run command: tar $exclude $opts $archive $src_path"
|
echo "Run command: tar $exclude $tar_options $archive $src_path"
|
||||||
}
|
}
|
||||||
|
|
||||||
log "Archiving $src_path to $archive ..."
|
log "Archiving $src_path to $archive ..."
|
||||||
log "Run command: tar $exclude $opts $archive $src_path"
|
log "Run command: tar $exclude $tar_options $archive $src_path"
|
||||||
|
|
||||||
# Run tar
|
# Run tar
|
||||||
try tar "$exclude" "$opts" "$archive" "$src_path"
|
try tar "$exclude" "$tar_options" "$archive" "$src_path"
|
||||||
|
|
||||||
# Append path to 'backups' array
|
# Append path to 'backups' array
|
||||||
backups+=("$archive")
|
backups+=("$archive")
|
||||||
|
@ -29,6 +29,7 @@ handler::cp() {
|
|||||||
|
|
||||||
uri="$1"
|
uri="$1"
|
||||||
|
|
||||||
|
# shellcheck disable=SC2154
|
||||||
if [[ "$uri" == "$__main_target" ]]; then
|
if [[ "$uri" == "$__main_target" ]]; then
|
||||||
: # Do nothing. Source and destination is the same
|
: # Do nothing. Source and destination is the same
|
||||||
log "Nothing to do: Source and destination is the same: $__main_target"
|
log "Nothing to do: Source and destination is the same: $__main_target"
|
||||||
@ -36,6 +37,7 @@ handler::cp() {
|
|||||||
# Copy backups to another destination
|
# Copy backups to another destination
|
||||||
parse_uri "$uri"
|
parse_uri "$uri"
|
||||||
|
|
||||||
|
# shellcheck disable=SC2154
|
||||||
if [ -f "$path" ] || [ -d "$path" ]; then
|
if [ -f "$path" ] || [ -d "$path" ]; then
|
||||||
dst_path="$path"
|
dst_path="$path"
|
||||||
else
|
else
|
||||||
@ -46,6 +48,7 @@ handler::cp() {
|
|||||||
[ "$__verbose" ] && echo "Destination path: $dst_path"
|
[ "$__verbose" ] && echo "Destination path: $dst_path"
|
||||||
|
|
||||||
# Copy files preserving metadata
|
# Copy files preserving metadata
|
||||||
|
# shellcheck disable=SC2154
|
||||||
for backup in "${backups[@]}"; do
|
for backup in "${backups[@]}"; do
|
||||||
log "Copying file $backup to $dst_path ..."
|
log "Copying file $backup to $dst_path ..."
|
||||||
log "Run command: cp --archive $backup $dst_path"
|
log "Run command: cp --archive $backup $dst_path"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
|
||||||
# lib.sh - source scripts.
|
# lib.sh - source scripts.
|
||||||
# Copyright (c) 2022 ge <https://nixhacks.net/>
|
# Copyright (c) 2022 ge <https://nixhacks.net/>
|
||||||
|
@ -35,7 +35,8 @@ validate_sources() {
|
|||||||
|
|
||||||
case "$scheme" in
|
case "$scheme" in
|
||||||
file|mysql|postgres|sqlite) : ;; # do nothing, this is OK
|
file|mysql|postgres|sqlite) : ;; # do nothing, this is OK
|
||||||
*) echo "Error: $__user_script: Unsupported URI scheme: $scheme" >&2; exit 1;;
|
*) # shellcheck disable=SC2154
|
||||||
|
echo "Error: $__user_script: Unsupported URI scheme: $scheme" >&2; exit 1;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
@ -78,6 +79,7 @@ validate_targets() {
|
|||||||
__main_target="${file_targets[0]}"
|
__main_target="${file_targets[0]}"
|
||||||
# Fail if __main_target's path is not a directory
|
# Fail if __main_target's path is not a directory
|
||||||
parse_uri "$__main_target"
|
parse_uri "$__main_target"
|
||||||
|
# shellcheck disable=SC2154
|
||||||
if [ -d "$path" ]; then
|
if [ -d "$path" ]; then
|
||||||
__main_target_path="$path"
|
__main_target_path="$path"
|
||||||
else
|
else
|
||||||
@ -99,24 +101,25 @@ source_script() {
|
|||||||
echo "Error: No such file: $script" >&2; exit 1
|
echo "Error: No such file: $script" >&2; exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Dry run script, check syntax. See set(1p)
|
# Dry run script, check syntax. See manpage set(1)
|
||||||
if ! bash -n "$script"; then
|
if ! bash -n "$script"; then
|
||||||
echo Error: $__user_script: Please check your syntax >&2; exit 1
|
echo "Error: $__user_script: Please check your syntax" >&2; exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Source script
|
# Source script
|
||||||
. "$@"
|
# shellcheck disable=SC1090
|
||||||
|
. "$script"
|
||||||
|
|
||||||
# Check required variables
|
# Check required variables
|
||||||
if [[ "$sources" ]]; then
|
if [ -n "$sources" ]; then
|
||||||
validate_sources "${sources[@]}"
|
validate_sources "${sources[@]}"
|
||||||
else
|
else
|
||||||
echo Error: $__user_script: sources array is not set >&2; exit 1
|
echo "Error: $__user_script: sources array is not set" >&2; exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$targets" ]]; then
|
if [ -n "$targets" ]; then
|
||||||
validate_targets "${targets[@]}"
|
validate_targets "${targets[@]}"
|
||||||
else
|
else
|
||||||
echo Error: $__user_script: targets array is not set >&2; exit 1
|
echo "Error: $__user_script: targets array is not set" >&2; exit 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ parse_uri() {
|
|||||||
# URL-encoded passwords supported.
|
# URL-encoded passwords supported.
|
||||||
#
|
#
|
||||||
# Usage: parse_uri URI
|
# Usage: parse_uri URI
|
||||||
# Return variables:
|
# Set variables:
|
||||||
# scheme
|
# scheme
|
||||||
# username
|
# username
|
||||||
# password
|
# password
|
||||||
@ -113,6 +113,7 @@ parse_uri() {
|
|||||||
port=
|
port=
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
|
# shellcheck disable=SC2034
|
||||||
hostname="$host"
|
hostname="$host"
|
||||||
port=
|
port=
|
||||||
fi
|
fi
|
||||||
@ -122,6 +123,7 @@ parse_uri() {
|
|||||||
username="${userinfo%:*}"
|
username="${userinfo%:*}"
|
||||||
password="${userinfo#*:}"
|
password="${userinfo#*:}"
|
||||||
else
|
else
|
||||||
|
# shellcheck disable=SC2034
|
||||||
username="$userinfo"
|
username="$userinfo"
|
||||||
password=
|
password=
|
||||||
fi
|
fi
|
||||||
|
Loading…
Reference in New Issue
Block a user