feat: Add variable name_suffix; Pass shellcheck; Discard useless variables from main script

This commit is contained in:
ge 2022-06-29 21:27:35 +03:00
parent 3d514678e7
commit aaa7a5c2d3
11 changed files with 177 additions and 147 deletions

View File

@ -23,8 +23,10 @@ tests:
for test in $(tests_dir)/*.bats; do bats --verbose-run --print-output-on-failure "$$test"; done
lint:
shellcheck $(src_dir)/boring-backup || true
shellcheck $(src_dir)/lib/* || true
shellcheck $(src_dir)/boring-backup
shellcheck $(src_dir)/lib/*.sh
shellcheck $(src_dir)/lib/handlers/sources/*.sh
shellcheck $(src_dir)/lib/handlers/targets/*.sh
docs: builddir
# See rst2man(1), rst2html(1), https://docutils.sourceforge.io/docs/index.html

View File

@ -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. В любом варианте применения утилита требует написания сценария для выполнения бэкапа.
В сценариях можно использовать переменные и функции. Сценарий импортируется в основной скрипт с помощью команды ``source`` (см. ``bash``\(1) п. SHELL BUILTIN COMMANDS).
boring-backup можно рассматривать как фреймворк или библиотеку для создания сценариев резервного копирования. Сценарии должны содержать валидный код на Bash. Сценарий импортируется в основной скрипт с помощью команды ``source`` (см. ``bash``\(1) п. SHELL BUILTIN COMMANDS).
Простейший сценарий резервного копирования выглядит следующим образом::
sources=('file:/home/user')
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
---------
@ -101,8 +99,9 @@ Perl 5::
``````````````````````````````````````````````````
file
Может содержать путь к локальному файлу или директории. Файлы архивируются при помощи ``tar`` и по умолчанию сжимаются с помощью ``gzip``. Примеры::
Может содержать путь к локальному файлу или директории. Файлы архивируются при помощи ``tar`` и по умолчанию не сжимаются. Cжатие архива может быть включено через переменную `compression`. Примеры::
/var/www/www-root/data
file:/var/www/html
file:///home/jhon/cool_stuff.txt
@ -115,17 +114,17 @@ mysql
mysql://[username[:password]@]hostname[:port]/database
С помощью утилиты ``mysqldump``\(1) формируется дамп в формате SQL, файл по умолчанию сжимается утилитой ``gzip``.
С помощью утилиты ``mysqldump``\(1) формируется дамп в формате SQL. Файл по умолчанию не сжимается, но сжатие может быть включено через переменную `compression`.
См. также ``handler::mysqldump``
postgres
Аналогично `mysql`, но для PostgreSQL. Для создания дампа используется ``pg_dump``\(1), файлы по умолчанию сохраняются с расширением .psql.gz.
Аналогично `mysql`, но для PostgreSQL. Для создания дампа используется ``pg_dump``\(1) с расширением .psql. Файл по умолчанию не сжимается, но сжатие может быть включено через переменную `compression`.
См. также ``handler::pg_dump``
sqlite
Схема для баз данных SQLite. Ничем не отличается от `file`, добавлена для наглядного обозначения, что источником является БД SQLite, а не иной файл.
Схема для баз данных SQLite. Мало отличается от `file`, добавлена для наглядного обозначения, что источником является БД SQLite, а не иной файл.
См. также ``handler::sqlite``
@ -133,7 +132,7 @@ sqlite
``````````````````````````````````````````````````
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``.
Базовая концепция строится на том, что существует несколько точек источников и несколько точек назначения. Создаётся резервная копия источника и копируется в точку назначения.
Базовая концепция строится на том, что существует несколько источников и несколько точек назначения. Создаётся резервная копия источника и копируется в точку назначения.
Для всех источников в сценарии выполняется локальная резервная копия. После этого локальная копия переносится в удалённые точки назначения, если они заданы. Копирование локального бэкапа будет осуществлёно столько раз, сколько точек назначения задано. В каждом сценарии обязательно должен быть задан хотя бы один источник и одна точка назначения. При этом в списке точек назначения обязательно должна присутствовать точка со схемой `file`. Именно в неё будут сохранены архивы.
Пример. Имеется следующий сценарий::
Например, имеется следующий сценарий::
sources=(
'mysql://wordpress:1jobrRRjtLYs@localhost/wordpress'
@ -212,80 +211,89 @@ WebDAV
``backups``
Массив со списком созданных бэкапов. Содержит абсолютные пути к файлам. Заполняется функциями-обработчиками `sources`.
Тип: массив
Умолчание: ()
| Тип: массив
| Умолчание: ()
``errors``
Массив с текстами ошибок. Заполняется функцией ``err`` с опцией ``-a``.
Тип: массив
Умолчание: ()
| Тип: массив
| Умолчание: ()
``log_date_fmt``
Формат даты в логе. См. ``date``\(1).
Тип: строка
Умолчание: %d/%b/%Y:%H:%M:%S %z
| Тип: строка
| Умолчание: %d/%b/%Y:%H:%M:%S %z
``name_prefix``
Префикс имени файла. Может быть задан в скрипте.
Тип: строка
Умолчание: имя_скрипта\_
| Тип: строка
| Умолчание: имя_скрипта\_
``name``
Соответствует имени архивируемой директории/файла полученного из `path` с помощью утилиты ``basename``\(1).
Тип: строка
Умолчание: нет
| Тип: строка
| Умолчание: нет
``name_date_fmt``
Дата, интерпретируемая утилитой ``date``\(1).
Тип: строка
Умолчание: _%Y%m%d-%H%M
| Тип: строка
| Умолчание: _%Y%m%d
``name_suffix``
Суффикс, добавляемый к имени файла перед расширением. Строка интерпретируется утилитой ``date``.
| Тип: строка
| Умолчание: -%H%M
``name_ext``
Расширение имени файла, соответствующее формату архива.
Тип: строка
Умолчание: .tar.gz
| Тип: строка
| Умолчание: .tar.gz
``tar_options``
Опции ``tar``. См. ``tar``\(1).
Тип: строка
Умолчание: -acf
| Тип: строка
| Умолчание: -acf
``tar_exclude``
Список имён для опции ``tar`` ``--exclude``.
Тип: массив
Умолчание: нет
| Тип: массив
| Умолчание: нет
``compression``
В контексте ``tar`` работа этой фичи базируется на опции ``tar`` ``--auto-compress``. Переменная может принимать значения, в соответствии с табилцей ниже. Если переменная имеет значение, отличное от перечисленныых, то будет использован ``gzip``.
В контексте ``tar`` работа этой фичи базируется на опции ``tar`` ``--auto-compress``. Переменная может принимать значения, в соответствии с табилцей ниже. Если переменная имеет значение, отличное от перечисленныых, то будет использован ``gzip``. Если переменная пуста или не задана, то сжатие будет отключено.
======================== ================ ========
Значение tar_compression Расширение файла Утилита
======================== ================ ========
gzip, gz .tar.gz gzip
tgz .tgz gzip
taz .taz gzip
compress, Z .tar.Z compress
taZ .taZ compress
bzip2, bz2 .tar.bz2 bzip2
tz2 .tz2 bzip2
tbz2 .tbz2 bzip2
tbz .tbz bzip2
lzip, lz .tar.lz lzip
lzma .tar.lzma lzma
tlz .tlz lzma
lzop, lzo .tar.lzo lzop
xz .tar.xz xz
zstd, zst .tar.zst zstd
tzst .tzst zstd
======================== ================ ========
| Тип: строка
| Умолчание: нет
==================== ================ ========
Значение compression Расширение файла Утилита
==================== ================ ========
gzip, gz .tar.gz gzip
tgz .tgz gzip
taz .taz gzip
compress, Z .tar.Z compress
taZ .taZ compress
bzip2, bz2 .tar.bz2 bzip2
tz2 .tz2 bzip2
tbz2 .tbz2 bzip2
tbz .tbz bzip2
lzip, lz .tar.lz lzip
lzma .tar.lzma lzma
tlz .tlz lzma
lzop, lzo .tar.lzo lzop
xz .tar.xz xz
zstd, zst .tar.zst zstd
tzst .tzst zstd
==================== ================ ========
Функции
-------

View File

@ -20,21 +20,17 @@ __version='0.0.0'
__config=
__verbose=
__log_file='./log.txt'
__log_date_fmt='%d/%b/%Y:%H:%M:%S %z'
__pid_file='/tmp/boring-backup.pid'
__tar_options='-acf'
__tar_exclude=
__name_date_fmt='_%Y%m%d-%H%M'
__compression=
__errors_count=0
__errors_file='/tmp/boring-backup.errors'
echo $__errors_count > $__errors_file
echo "$__errors_count" > "$__errors_file"
LIBRARY="${LIBRARY:-./lib}"
# Source library
if [ -f "$LIBRARY/lib.sh" ]; then
# shellcheck disable=SC1091
. "$LIBRARY/lib.sh"
else
echo "Error: Cannot source library from $LIBRARY" >&2
@ -69,7 +65,7 @@ optval() {
#
# Parse --opt VAL and --opt=VAL options.
# Requires 2 arguments: $1, $2.
# Return variables:
# Set variables:
# opt option name.
# val option value.
# sft value for shift.
@ -96,7 +92,8 @@ for args in "$@"; do
set -- "$@" "$args";; # save long options
-*)
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
esac
@ -123,20 +120,22 @@ done
log "Backup STARTED"
# Check PID file
if [ -e $__pid_file ]; then
if [ -z "$(ps ax -o pid | grep "$(cat $__pid_file)")" ]; then
err "Process $(cat $__pid_file) died."
rm $__pid_file
if [ -e "$__pid_file" ]; then
# shellcheck disable=SC2009
# shellcheck disable=SC2143
if [ -z "$(ps ax -o pid | grep "$(cat "$__pid_file")")" ]; then
err "Process $(cat "$__pid_file") died."
rm "$__pid_file"
else
err -e "Process $(cat $__pid_file) still running.";
err -e "Process $(cat "$__pid_file") still running.";
fi
fi
# Touch PID file
echo $$ > $__pid_file
echo "$$" > "$__pid_file"
# Scripts counter.
__count=${#__args[@]} # count
__count="${#__args[@]}" # count
__iter=1 # iterator
# Startup log.
@ -144,18 +143,22 @@ date +'Start: %d %b %Y %T %z'
log -p "Library path: $LIBRARY"
log -p "Log file: $__log_file"
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
# Initialise variables
__user_script="$script"
# shellcheck disable=SC2034
backups=() # Array of created backups, contains full pathes
# shellcheck disable=SC2034
errors=() # Array of error messages written by err() function
# Source scripts
source_script "$script"
# Config can ovewrite script functions and variables
# shellcheck source=/dev/null
[ -n "$__config" ] && . "$__config"
echo
@ -209,7 +212,7 @@ if [[ "$(cat $__errors_file)" != '0' ]]; then
echo
log -p "Backup [Failed]: Process finished with $(cat $__errors_file) errors."\
"See $__log_file for info."
rm $__errors_file
rm "$__errors_file"
else
echo -e "\nBackup [Done]"
fi
@ -217,4 +220,4 @@ fi
log "Backup FINISHED"
# Remove PID file
rm $__pid_file
rm "$__pid_file"

View File

@ -37,7 +37,9 @@ process_source() {
mysql) handler='handler::mysqldump';;
postgres) handler='handler::pg_dump';;
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
# Run handler function
@ -84,10 +86,12 @@ builtin_backup() {
#
# Usage: builtin_backup
# shellcheck disable=SC2154
for source in "${sources[@]}"; do
process_source "$source"
done
# shellcheck disable=SC2154
for target in "${targets[@]}"; do
process_target "$target"
done
@ -102,21 +106,15 @@ gen_backup_name() {
#
# Usage: gen_backup_name NAME_EXT
local prefix
local name
local date_fmt
local name_ext
[ -n "$name_prefix" ] || { prefix="${__user_script}_"; }
name="$(basename $path)" # 'path' is variable parsed from URI
name_prefix="${name_prefix:-${__user_script}_}"
# shellcheck disable=SC2154
name="$(basename "$path")" # 'path' is variable parsed from URI
name_ext="$1"
name_date_fmt="${name_date_fmt:-_%Y.%m.%d}"
name_suffix="${name_suffix:--%H%M}"
# Overwrite __name_date_fmt
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}"
date +"${name_prefix}${name}${name_date_fmt}${name_suffix}${name_ext}"
}

View File

@ -31,7 +31,7 @@ log() {
[[ ! -t 0 ]] || [[ "$#" == 0 ]] && message="$(cat <&0)"
# 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
case "$1" in
@ -44,7 +44,8 @@ log() {
[[ "$print" == 1 ]] && echo -e "$message"
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"
fi
done <<< "$(sed -r 's/\x1B\[(([0-9]+)(;[0-9]+)*)?[m,K,H,f,J]//g' <<< "$message")"
@ -69,7 +70,8 @@ err() {
case "$args" in
-*)
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
esac
@ -100,9 +102,10 @@ err() {
# Exit
if [ "$errexit" ]; then
# Count errors for backup recap
__errors_count="$(cat $__errors_file)"
# shellcheck disable=SC2154
__errors_count="$(cat "$__errors_file")"
((__errors_count++)) || true
echo $__errors_count > "$__errors_file"
echo "$__errors_count" > "$__errors_file"
log -p "Backup ERROR: Exiting with previous errors" >&2; exit 1
fi
@ -113,7 +116,8 @@ try() {
#
# Usage: try COMMAND
if eval "$@" 2>> "$__log_file"; then
# https://www.shellcheck.net/wiki/SC2294
if "$@" 2>> "$__log_file"; then
return 0 # Success
else
# Count errors
@ -168,6 +172,11 @@ set_compression() {
xz) file_ext='.xz'; compr_util='xz';;
zstd|zst) 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
}

View File

@ -32,12 +32,14 @@ handler::mysqldump() {
local file_ext
uri="$1"
# shellcheck disable=SC2154
dst_path="$__main_target_path"
parse_uri "$uri"
[ -z "$port" ] && port=3306 # Set default MySQL port.
# shellcheck disable=SC2154
[ -n "$__verbose" ] && echo "Dumping database '${path##*/}'"\
"owned by ${username}@${hostname} ..." | log -p
@ -61,7 +63,10 @@ handler::mysqldump() {
compr_cmd="cat"
fi
mysqldump_opts="${mysqldump_opts:-}"
# NOTE! mysqldump_opts and compr_cmd variables must be unquoted!
# shellcheck disable=SC2086,SC2154
try mysqldump $mysqldump_opts \
--host="$hostname" \
--port="$port" \
@ -76,33 +81,32 @@ handler::mysqldump() {
fi
}
# POSTGRESQL vvv
bk_dump_postgresql() {
# Do PostgreSQL dump.
bk_log "Dumping database '${path##*/}' owned by ${user}@${host} (PostgreSQL) ..."
dump_name="$entry_local"/"$(bk_get_name "$db_name" .psql.gz)"
[ "$port" ] || port=5432 # Set default PostgreSQL port.
export PGPASSWORD="$password"
pg_dump \
--host="$host" \
--port="$port" \
--dbname="${path##*/}" \
--username="$user" \
--no-password | gzip -c > "$dump_name" |& bk_log
unset PGPASSWORD
if [ -s "$dump_name" ]; then
bk_log "Dumping database '${path##*/}' owned by ${user}@${host} (PostgreSQL) [Done]"
bk_log "Dump saved as: $dump_name"
bk_upload_file "$dump_name"
else
rm "$dump_name"
bk_err "Something went wrong. Dump size is 0 bytes. Removing $dump_name"
fi
}
# POSTGRESQL
#bk_dump_postgresql() {
# # Do PostgreSQL dump.
#
# bk_log "Dumping database '${path##*/}' owned by ${user}@${host} (PostgreSQL) ..."
#
# dump_name="$entry_local"/"$(bk_get_name "$db_name" .psql.gz)"
# [ "$port" ] || port=5432 # Set default PostgreSQL port.
# export PGPASSWORD="$password"
#
# pg_dump \
# --host="$host" \
# --port="$port" \
# --dbname="${path##*/}" \
# --username="$user" \
# --no-password | gzip -c > "$dump_name" |& bk_log
#
# unset PGPASSWORD
#
# if [ -s "$dump_name" ]; then
# bk_log "Dumping database '${path##*/}' owned by ${user}@${host} (PostgreSQL) [Done]"
# bk_log "Dump saved as: $dump_name"
# bk_upload_file "$dump_name"
#
# else
# rm "$dump_name"
# bk_err "Something went wrong. Dump size is 0 bytes. Removing $dump_name"
# fi
#}

View File

@ -26,16 +26,17 @@ handler::tar() {
local uri
local src_path
local dst_path
local opts
local archive
local exclude
local file_ext
uri="$1"
# shellcheck disable=SC2154
dst_path="$__main_target_path"
parse_uri "$uri"
# shellcheck disable=SC2154
if [ -f "$path" ] || [ -d "$path" ]; then
src_path="$path"
else
@ -51,15 +52,8 @@ handler::tar() {
# Exit if tar is not installed
is_installed tar
# Overwrire __tar_options
if [ -n "$tar_options" ]; then
opts="$tar_options"
else
opts="$__tar_options"
fi
# Overwrite __tar_exclude
if [ "$tar_exclude" ]; then
# Set tar_exclude
if [ -n "$tar_exclude" ]; then
for item in "${tar_exclude[@]}"; do
exclude+=" --exclude $item"
done
@ -67,6 +61,9 @@ handler::tar() {
exclude=
fi
# Set options
tar_options="${tar_options:--acf}"
# Select filename extension by compression type.
if [ "$compression" ]; then
# Make sure for the `--auto-compress` is enabled in __tar_options
@ -99,14 +96,14 @@ handler::tar() {
[ "$__verbose" ] && {
echo "Source path: $src_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 "Run command: tar $exclude $opts $archive $src_path"
log "Run command: tar $exclude $tar_options $archive $src_path"
# Run tar
try tar "$exclude" "$opts" "$archive" "$src_path"
try tar "$exclude" "$tar_options" "$archive" "$src_path"
# Append path to 'backups' array
backups+=("$archive")

View File

@ -29,6 +29,7 @@ handler::cp() {
uri="$1"
# shellcheck disable=SC2154
if [[ "$uri" == "$__main_target" ]]; then
: # Do nothing. Source and destination is the same
log "Nothing to do: Source and destination is the same: $__main_target"
@ -36,6 +37,7 @@ handler::cp() {
# Copy backups to another destination
parse_uri "$uri"
# shellcheck disable=SC2154
if [ -f "$path" ] || [ -d "$path" ]; then
dst_path="$path"
else
@ -46,6 +48,7 @@ handler::cp() {
[ "$__verbose" ] && echo "Destination path: $dst_path"
# Copy files preserving metadata
# shellcheck disable=SC2154
for backup in "${backups[@]}"; do
log "Copying file $backup to $dst_path ..."
log "Run command: cp --archive $backup $dst_path"

View File

@ -1,4 +1,5 @@
#!/usr/bin/env bash
# shellcheck disable=SC1091
# lib.sh - source scripts.
# Copyright (c) 2022 ge <https://nixhacks.net/>

View File

@ -35,7 +35,8 @@ validate_sources() {
case "$scheme" in
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
done
}
@ -78,6 +79,7 @@ validate_targets() {
__main_target="${file_targets[0]}"
# Fail if __main_target's path is not a directory
parse_uri "$__main_target"
# shellcheck disable=SC2154
if [ -d "$path" ]; then
__main_target_path="$path"
else
@ -99,24 +101,25 @@ source_script() {
echo "Error: No such file: $script" >&2; exit 1
fi
# Dry run script, check syntax. See set(1p)
# Dry run script, check syntax. See manpage set(1)
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
# Source script
. "$@"
# shellcheck disable=SC1090
. "$script"
# Check required variables
if [[ "$sources" ]]; then
if [ -n "$sources" ]; then
validate_sources "${sources[@]}"
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
if [[ "$targets" ]]; then
if [ -n "$targets" ]; then
validate_targets "${targets[@]}"
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
}

View File

@ -23,7 +23,7 @@ parse_uri() {
# URL-encoded passwords supported.
#
# Usage: parse_uri URI
# Return variables:
# Set variables:
# scheme
# username
# password
@ -113,6 +113,7 @@ parse_uri() {
port=
fi
else
# shellcheck disable=SC2034
hostname="$host"
port=
fi
@ -122,6 +123,7 @@ parse_uri() {
username="${userinfo%:*}"
password="${userinfo#*:}"
else
# shellcheck disable=SC2034
username="$userinfo"
password=
fi