2023-12-07 04:02:47 +03:00
|
|
|
export _archive_pattern="_[0-9]{12}-[[:alnum:]]{40}.t[xg]z"
|
2023-12-16 22:52:36 +03:00
|
|
|
export _archive_pattern_fast="_[0-9]{12}-[[:alnum:]]{40}.tgz"
|
2023-12-07 01:44:42 +03:00
|
|
|
|
2023-12-07 04:02:47 +03:00
|
|
|
# Archive directories.
|
|
|
|
# All directories by default.
|
2024-01-03 17:21:11 +03:00
|
|
|
# Supports .archiveignore exclude file.
|
2023-12-07 04:02:47 +03:00
|
|
|
# Usage: archive [DIRS]
|
2023-12-07 01:44:42 +03:00
|
|
|
function archive() {
|
2023-12-05 21:50:45 +03:00
|
|
|
local IFS=$'\n'
|
2023-12-16 22:52:36 +03:00
|
|
|
local targets=(${@})
|
|
|
|
[[ "${targets}" = "" ]] && targets=($(_ls_dir))
|
2023-12-05 21:50:45 +03:00
|
|
|
|
2023-12-16 22:52:36 +03:00
|
|
|
process() {
|
|
|
|
local date=$(_archive_date)
|
2023-12-05 21:50:45 +03:00
|
|
|
|
2023-12-16 22:52:36 +03:00
|
|
|
# Parse name.
|
2023-12-17 23:46:41 +03:00
|
|
|
local name=$(parse_pascal ${target})
|
2023-12-05 21:50:45 +03:00
|
|
|
|
2024-01-03 17:21:11 +03:00
|
|
|
# Exclude support.
|
|
|
|
local exclude=""
|
|
|
|
[[ -f ".archiveignore" ]] && exclude="--exclude-from=.archiveignore"
|
|
|
|
[[ -f "${target}/.archiveignore" ]] && exclude="--exclude-from=${target}/.archiveignore"
|
|
|
|
|
2023-12-05 21:50:45 +03:00
|
|
|
# create archive.
|
2024-01-03 17:21:11 +03:00
|
|
|
local hash=$(tar ${exclude} -c ${target} | pv -s $(/usr/bin/du -sb ${target} | awk '{print $1}') | xz -9e | tee ${name}.txz | sha1sum | cut -d\ -f1)
|
2023-12-10 04:31:03 +03:00
|
|
|
|
2024-01-03 16:40:51 +03:00
|
|
|
# append hash to target name.
|
|
|
|
local new_name="${name}_${date}-${hash}.txz"
|
|
|
|
mv -- ${name}.txz ${new_name} && echo ${new_name}
|
|
|
|
}
|
|
|
|
|
|
|
|
_iterate_targets process ${targets[@]}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Archive using multiple threads. Uses 50% of free RAM.
|
|
|
|
# All directories by default.
|
2024-01-03 17:21:11 +03:00
|
|
|
# Supports .archiveignore exclude file.
|
2024-01-03 16:40:51 +03:00
|
|
|
# Usage: archive_mt [DIRS]
|
|
|
|
function archive_mt() {
|
|
|
|
local IFS=$'\n'
|
|
|
|
local targets=(${@})
|
|
|
|
[[ "${targets}" = "" ]] && targets=($(_ls_dir))
|
|
|
|
|
|
|
|
process() {
|
|
|
|
local date=$(_archive_date)
|
|
|
|
|
|
|
|
# Parse name.
|
|
|
|
local name=$(parse_pascal ${target})
|
|
|
|
|
2024-01-03 17:21:11 +03:00
|
|
|
# Exclude support.
|
|
|
|
local exclude=""
|
|
|
|
[[ -f ".archiveignore" ]] && exclude="--exclude-from=.archiveignore"
|
|
|
|
[[ -f "${target}/.archiveignore" ]] && exclude="--exclude-from=${target}/.archiveignore"
|
|
|
|
|
2024-01-03 16:40:51 +03:00
|
|
|
# Determine memory limit.
|
|
|
|
local mem_free=$(_mem_free)
|
|
|
|
local mem_limit=$((mem_free/2))
|
|
|
|
|
|
|
|
# create archive.
|
2024-01-03 17:21:11 +03:00
|
|
|
local hash=$(tar ${exclude} -c ${target} | pv -s $(/usr/bin/du -sb ${target} | awk '{print $1}') | xz -9e --threads=0 --memlimit=${mem_limit}MiB | tee ${name}.txz | sha1sum | cut -d\ -f1)
|
2024-01-03 16:40:51 +03:00
|
|
|
|
2023-12-05 21:50:45 +03:00
|
|
|
# append hash to target name.
|
2023-12-17 16:27:21 +03:00
|
|
|
local new_name="${name}_${date}-${hash}.txz"
|
|
|
|
mv -- ${name}.txz ${new_name} && echo ${new_name}
|
2023-12-16 22:52:36 +03:00
|
|
|
}
|
2023-12-05 21:50:45 +03:00
|
|
|
|
2023-12-16 22:52:36 +03:00
|
|
|
_iterate_targets process ${targets[@]}
|
2023-08-08 16:24:15 +03:00
|
|
|
}
|
|
|
|
|
2023-12-07 04:02:47 +03:00
|
|
|
# Archive directories with fast compression.
|
|
|
|
# All directories by default.
|
2024-01-03 17:21:11 +03:00
|
|
|
# Supports .archiveignore exclude file.
|
2023-12-07 04:02:47 +03:00
|
|
|
# Usage: archive_fast [DIRS]
|
2023-12-07 01:44:42 +03:00
|
|
|
function archive_fast() {
|
2023-12-05 21:50:45 +03:00
|
|
|
local IFS=$'\n'
|
2023-12-06 02:31:52 +03:00
|
|
|
local targets=("${@}")
|
2023-12-16 22:52:36 +03:00
|
|
|
[[ "${targets}" = "" ]] && targets=($(_ls_dir))
|
2023-12-05 21:50:45 +03:00
|
|
|
|
2023-12-16 22:52:36 +03:00
|
|
|
process() {
|
2023-12-17 16:27:21 +03:00
|
|
|
# Start timestamp.
|
2023-12-16 22:52:36 +03:00
|
|
|
local date=$(_archive_date)
|
2023-12-05 21:50:45 +03:00
|
|
|
|
2023-12-16 22:52:36 +03:00
|
|
|
# Parse name.
|
2023-12-17 23:46:41 +03:00
|
|
|
local name=$(parse_pascal "${target}")
|
2023-12-05 21:50:45 +03:00
|
|
|
|
2024-01-03 17:21:11 +03:00
|
|
|
# Exclude support.
|
|
|
|
local exclude=""
|
|
|
|
[[ -f ".archiveignore" ]] && exclude="--exclude-from=.archiveignore"
|
|
|
|
[[ -f "${target}/.archiveignore" ]] && exclude="--exclude-from=${target}/.archiveignore"
|
|
|
|
|
2023-12-05 21:50:45 +03:00
|
|
|
# create archive.
|
2024-01-03 17:21:11 +03:00
|
|
|
local hash=$(tar ${exclude} -c "${target}" | pv -s $(/usr/bin/du -sb "${target}" | awk '{print $1}') | gzip -1 | tee "${name}".tgz | sha1sum | cut -d\ -f1)
|
2023-12-10 04:31:03 +03:00
|
|
|
|
2023-12-05 21:50:45 +03:00
|
|
|
# append hash to target name.
|
2023-12-17 16:27:21 +03:00
|
|
|
local new_name="${name}_${date}-${hash}.tgz"
|
|
|
|
mv -- "${name}".tgz ${new_name} && echo ${new_name}
|
2023-12-16 22:52:36 +03:00
|
|
|
}
|
2023-12-05 21:50:45 +03:00
|
|
|
|
2023-12-16 22:52:36 +03:00
|
|
|
_iterate_targets process ${targets[@]}
|
2023-08-08 16:24:15 +03:00
|
|
|
}
|
|
|
|
|
2023-12-07 04:02:47 +03:00
|
|
|
# Check archives integrity.
|
|
|
|
# Checks all archives by default.
|
|
|
|
# Usage: archive_check [FILES]
|
2023-12-07 01:44:42 +03:00
|
|
|
function archive_check() {
|
2023-12-05 21:50:45 +03:00
|
|
|
local IFS=$'\n'
|
2023-12-16 22:52:36 +03:00
|
|
|
local targets=(${@})
|
2024-01-03 18:39:38 +03:00
|
|
|
[[ "${targets}" = "" ]] && targets=($(_ls_archive))
|
2023-12-05 21:50:45 +03:00
|
|
|
|
2023-12-16 22:52:36 +03:00
|
|
|
process() {
|
2023-12-05 21:50:45 +03:00
|
|
|
# extract hash from name.
|
|
|
|
local data=($(_archive_parse ${target}))
|
|
|
|
local saved=${data[2]}
|
|
|
|
|
|
|
|
# calculate actual hash.
|
2023-12-16 22:52:36 +03:00
|
|
|
local actual=$(pv ${target} | sha1sum | cut -d\ -f1)
|
2023-12-05 21:50:45 +03:00
|
|
|
|
2023-12-16 22:52:36 +03:00
|
|
|
# compare hashes.
|
|
|
|
[[ "${actual}" = "${saved}" ]]
|
|
|
|
}
|
2023-12-05 21:50:45 +03:00
|
|
|
|
2023-12-16 22:52:36 +03:00
|
|
|
_iterate_targets process ${targets[@]}
|
2023-08-08 16:24:15 +03:00
|
|
|
}
|
|
|
|
|
2024-01-03 18:39:38 +03:00
|
|
|
# Delete old versions of an archive.
|
|
|
|
# All archives with 1 version by default.
|
|
|
|
# Usage: archive_prune [NAME] [VERSIONS]
|
2023-12-07 01:44:42 +03:00
|
|
|
function archive_prune() {
|
2023-12-05 21:50:45 +03:00
|
|
|
local IFS=$'\n'
|
2024-01-03 18:39:38 +03:00
|
|
|
local targets=(${1})
|
2024-01-03 18:06:05 +03:00
|
|
|
local versions=${2}
|
|
|
|
|
2024-01-03 18:39:38 +03:00
|
|
|
[[ "${targets}" = "" ]] && targets=($(_archive_names))
|
2024-01-03 18:06:05 +03:00
|
|
|
[[ "${versions}" = "" ]] && versions=1
|
|
|
|
|
2024-01-03 18:39:38 +03:00
|
|
|
if [[ ${#} -gt 2 ]]; then
|
|
|
|
help archive_prune
|
2024-01-03 18:06:05 +03:00
|
|
|
return 2
|
|
|
|
fi
|
|
|
|
|
2024-01-03 18:39:38 +03:00
|
|
|
process() {
|
|
|
|
local prune=($(ls ${target}* | _filter_archive | sort -r | sed -e "1,${versions}d"))
|
|
|
|
|
|
|
|
for archive in ${prune[@]}; do
|
|
|
|
rm -- "${archive}" && echo "Prune: ${archive}"
|
|
|
|
done
|
|
|
|
}
|
2024-01-03 18:06:05 +03:00
|
|
|
|
2024-01-03 18:39:38 +03:00
|
|
|
_iterate_targets process ${targets[@]}
|
2024-01-03 18:06:05 +03:00
|
|
|
}
|
|
|
|
|
2023-12-10 04:31:03 +03:00
|
|
|
# Delete specified or all archive files.
|
|
|
|
# Usage: archive_rm [FILES]
|
|
|
|
function archive_rm() {
|
|
|
|
local IFS=$'\n'
|
2023-12-16 22:52:36 +03:00
|
|
|
local targets=(${@})
|
2024-01-03 18:39:38 +03:00
|
|
|
[[ "${targets}" = "" ]] && targets=($(_ls_archive))
|
2023-12-10 04:31:03 +03:00
|
|
|
|
2023-12-16 22:52:36 +03:00
|
|
|
process() {
|
2023-12-17 16:27:21 +03:00
|
|
|
rm -- "${target}"
|
2023-12-16 22:52:36 +03:00
|
|
|
}
|
2023-12-10 04:31:03 +03:00
|
|
|
|
2023-12-16 22:52:36 +03:00
|
|
|
_iterate_targets process ${targets[@]}
|
2023-12-10 04:31:03 +03:00
|
|
|
}
|
|
|
|
|
2023-12-16 21:56:30 +03:00
|
|
|
# Recompress previously created archive_fast with better compression.
|
|
|
|
# Usage: archive_xz [FILES]
|
|
|
|
function archive_xz() {
|
2023-12-16 22:52:36 +03:00
|
|
|
local IFS=$'\n'
|
2023-12-17 16:27:21 +03:00
|
|
|
local targets=(${@})
|
2024-01-03 18:39:38 +03:00
|
|
|
[[ "${targets}" = "" ]] && targets=$(_ls_archive_fast)
|
2023-12-16 21:56:30 +03:00
|
|
|
|
2023-12-16 22:52:36 +03:00
|
|
|
process() {
|
2023-12-16 21:56:30 +03:00
|
|
|
local data=($(_archive_parse "${target}"))
|
|
|
|
local tmp="${data[0]}.txz"
|
2023-12-05 21:50:45 +03:00
|
|
|
|
2023-12-16 21:56:30 +03:00
|
|
|
# Check that old format.
|
|
|
|
if [[ "${data[3]}" != "tgz" ]]; then
|
2023-12-17 16:27:21 +03:00
|
|
|
_error "Not in .tgz format!"
|
2023-12-16 21:56:30 +03:00
|
|
|
return 1
|
|
|
|
fi
|
2023-11-30 02:25:10 +03:00
|
|
|
|
2023-12-16 21:56:30 +03:00
|
|
|
# Recompress.
|
|
|
|
local hash=$(pv "${target}" | gzip -d | xz -9e | tee "${tmp}" | sha1sum | cut -d\ -f1)
|
2023-12-05 21:50:45 +03:00
|
|
|
|
2023-12-16 21:56:30 +03:00
|
|
|
# Rename.
|
2023-12-17 16:27:21 +03:00
|
|
|
local new_name="${data[0]}_${data[1]}-${hash}.txz"
|
|
|
|
mv -- ${tmp} ${new_name} && rm ${target} && echo ${new_name}
|
|
|
|
}
|
|
|
|
|
|
|
|
_iterate_targets process ${targets[@]}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Rename archives.
|
|
|
|
# If no name specified, it simplifies archive's name.
|
|
|
|
# If no archives specified, apply to all archives.
|
|
|
|
# Usage: archive_name [ARCHIVE] [NAME]
|
|
|
|
function archive_name() {
|
|
|
|
local IFS=$'\n'
|
|
|
|
local targets="${1}"
|
|
|
|
local name="${2}"
|
2024-01-03 18:39:38 +03:00
|
|
|
[[ "${targets}" = "" ]] && targets=($(_ls_archive))
|
2023-12-17 16:27:21 +03:00
|
|
|
|
|
|
|
process() {
|
|
|
|
# simplify name by default.
|
|
|
|
if [[ "${name}" = "" || ${count} -gt 1 ]]; then
|
|
|
|
name="${target%_*}"
|
2023-12-17 23:46:41 +03:00
|
|
|
name="$(parse_pascal ${name})"
|
2023-12-17 16:27:21 +03:00
|
|
|
fi
|
|
|
|
|
|
|
|
# remove old name.
|
|
|
|
local data="${target##*_}"
|
|
|
|
local new_name="${name}_${data}"
|
|
|
|
|
|
|
|
# check for the same name.
|
|
|
|
[[ "${target}" = "${new_name}" ]] && return 0
|
|
|
|
|
|
|
|
# check for existing target.
|
|
|
|
if [[ -f "${new_name}" ]]; then
|
|
|
|
_error "${new_name}: Already exists!"
|
|
|
|
return 1
|
|
|
|
fi
|
|
|
|
|
|
|
|
# rename.
|
|
|
|
mv -- ${target} ${new_name} && echo ${new_name}
|
2023-12-16 21:56:30 +03:00
|
|
|
}
|
2023-12-05 21:50:45 +03:00
|
|
|
|
2023-12-16 21:56:30 +03:00
|
|
|
_iterate_targets process ${targets[@]}
|
2023-10-29 22:04:26 +03:00
|
|
|
}
|
|
|
|
|
2023-12-16 17:13:06 +03:00
|
|
|
# Extract previously created archive with checksum validation.
|
|
|
|
# Usage: unarchive [FILES]
|
|
|
|
function unarchive() {
|
|
|
|
local IFS=$'\n'
|
2023-12-16 22:52:36 +03:00
|
|
|
local targets=(${@})
|
2024-01-03 18:39:38 +03:00
|
|
|
[[ "${targets}" = "" ]] && targets=$(_ls_archive)
|
2023-12-16 17:13:06 +03:00
|
|
|
|
2023-12-16 22:52:36 +03:00
|
|
|
process() {
|
2023-12-16 17:13:06 +03:00
|
|
|
# extract hash from name.
|
2023-12-16 22:52:36 +03:00
|
|
|
local data=($(_archive_parse ${target}))
|
|
|
|
local saved=${data[2]}
|
2023-12-16 17:13:06 +03:00
|
|
|
|
|
|
|
# calculate actual hash.
|
2023-12-16 22:52:36 +03:00
|
|
|
local actual=$(pv ${target} | sha1sum | cut -d\ -f1)
|
2023-12-16 17:13:06 +03:00
|
|
|
|
|
|
|
# extract if hash matched or show error if not.
|
|
|
|
if [[ "${saved}" = "${actual}" ]]; then
|
|
|
|
case "${target##*.}" in
|
|
|
|
"txz")
|
2023-12-16 22:52:36 +03:00
|
|
|
pv ${target} | xz -d | tar -xf -
|
2023-12-16 17:13:06 +03:00
|
|
|
;;
|
|
|
|
"tgz")
|
2023-12-16 22:52:36 +03:00
|
|
|
pv ${target} | gzip -d | tar -xf -
|
2023-12-16 17:13:06 +03:00
|
|
|
;;
|
|
|
|
esac
|
|
|
|
else
|
2023-12-17 20:12:33 +03:00
|
|
|
_error "Failed."
|
2023-12-17 16:27:21 +03:00
|
|
|
return 1
|
2023-12-16 17:13:06 +03:00
|
|
|
fi
|
2023-12-16 22:52:36 +03:00
|
|
|
}
|
2023-12-16 17:13:06 +03:00
|
|
|
|
2023-12-16 22:52:36 +03:00
|
|
|
_iterate_targets process ${targets[@]}
|
2023-12-16 17:13:06 +03:00
|
|
|
}
|
|
|
|
|
2023-12-16 21:56:30 +03:00
|
|
|
# Parse archive file name to get: name, date, hash and format.
|
|
|
|
# Usage: _archive_parse <FILENAME>
|
|
|
|
function _archive_parse() {
|
|
|
|
local input="${1}"
|
|
|
|
local name="${input%_*}"
|
|
|
|
local format="${input##*.}"
|
|
|
|
local data="${input##*_}"; data="${data%.*}"
|
|
|
|
local date="${data%%-*}"
|
|
|
|
local hash="${data##*-}"
|
|
|
|
|
|
|
|
echo "${name}"
|
|
|
|
echo "${date}"
|
|
|
|
echo "${hash}"
|
|
|
|
echo "${format}"
|
|
|
|
}
|
|
|
|
|
|
|
|
# Autocomplete for archive_name function.
|
|
|
|
# First arg is the archives list, second one is selected archive's current name.
|
2024-01-03 18:39:38 +03:00
|
|
|
function _comp_archive_name() {
|
2023-12-16 21:56:30 +03:00
|
|
|
local IFS=$'\n'
|
|
|
|
COMPREPLY=()
|
|
|
|
|
|
|
|
local cur="${COMP_WORDS[COMP_CWORD]}"
|
|
|
|
local prev="${COMP_WORDS[COMP_CWORD-1]}"
|
|
|
|
local command="${COMP_WORDS[0]}"
|
|
|
|
|
|
|
|
if [[ "${prev}" = "${command}" ]]; then
|
|
|
|
COMPREPLY=( $(compgen -W "$(ls | grep -E ${_archive_pattern})" -- ${cur}) )
|
|
|
|
return 0
|
|
|
|
else
|
2024-01-03 18:06:05 +03:00
|
|
|
local data=($(_archive_parse ${prev}))
|
|
|
|
local name="${data[0]}"
|
2023-12-16 21:56:30 +03:00
|
|
|
COMPREPLY=( $(compgen -W "${name}" -- ${cur}) )
|
|
|
|
return 0
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2023-12-07 04:02:47 +03:00
|
|
|
# Autocomplete with archives in current dir.
|
2024-01-03 18:39:38 +03:00
|
|
|
function _comp_archive_grep() {
|
2023-12-07 04:02:47 +03:00
|
|
|
_autocomplete_grep ${_archive_pattern}
|
|
|
|
}
|
|
|
|
|
2023-12-16 22:52:36 +03:00
|
|
|
# Autocomplete with fast archives in current dir.
|
2024-01-03 18:39:38 +03:00
|
|
|
function _comp_archive_grep_fast() {
|
2023-12-16 22:52:36 +03:00
|
|
|
_autocomplete_grep ${_archive_pattern_fast}
|
|
|
|
}
|
|
|
|
|
2023-12-07 04:02:47 +03:00
|
|
|
# Get date for a new archive.
|
|
|
|
function _archive_date() {
|
|
|
|
date +%Y%m%d%H%M
|
2023-10-30 14:22:24 +03:00
|
|
|
}
|
|
|
|
|
2024-01-03 18:06:05 +03:00
|
|
|
# Get names of all archives.
|
|
|
|
function _archive_names() {
|
|
|
|
local IFS=$'\n'
|
2024-01-03 18:39:38 +03:00
|
|
|
local archives=($(_ls_archive))
|
2024-01-03 18:06:05 +03:00
|
|
|
local names=()
|
2024-01-03 18:39:38 +03:00
|
|
|
|
2024-01-03 18:06:05 +03:00
|
|
|
for archive in ${archives[@]}; do
|
|
|
|
local data=($(_archive_parse ${archive}))
|
2024-01-03 18:39:38 +03:00
|
|
|
names+=(${data[0]})
|
2024-01-03 18:06:05 +03:00
|
|
|
done
|
|
|
|
|
2024-01-03 18:39:38 +03:00
|
|
|
# Remove copies.
|
|
|
|
names=($(printf '%s\n' "${names[@]}" | sort -u))
|
2024-01-03 18:06:05 +03:00
|
|
|
|
2024-01-03 18:39:38 +03:00
|
|
|
printf '%s\n' "${names[@]}"
|
|
|
|
}
|
2024-01-03 18:06:05 +03:00
|
|
|
|
2024-01-03 18:39:38 +03:00
|
|
|
# Autocomplete with names of all archives.
|
|
|
|
function _comp_archive_names() {
|
|
|
|
_autocomplete $(_archive_names)
|
2024-01-03 18:06:05 +03:00
|
|
|
}
|
|
|
|
|
2023-12-27 14:05:07 +03:00
|
|
|
# Check if file is an archive.
|
|
|
|
function _is_archive() {
|
|
|
|
local out=$(echo "${*}" | grep -E ${_archive_pattern})
|
|
|
|
|
|
|
|
[[ "${out}" != "" ]]
|
|
|
|
}
|
|
|
|
|
2024-01-03 18:39:38 +03:00
|
|
|
# List all archives.
|
|
|
|
function _ls_archive() {
|
|
|
|
ls | grep -E ${_archive_pattern}
|
|
|
|
}
|
|
|
|
|
|
|
|
# List fast archives.
|
|
|
|
function _ls_archive_fast() {
|
|
|
|
ls | grep -E ${_archive_pattern_fast}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Filter input for archives only.
|
|
|
|
function _filter_archive() {
|
|
|
|
grep -E ${_archive_pattern}
|
|
|
|
}
|
|
|
|
|
|
|
|
complete -o filenames -F _comp_archive_grep archive_check unarchive archive_rm
|
|
|
|
complete -o filenames -F _comp_archive_grep_fast archive_xz
|
|
|
|
complete -o filenames -F _comp_archive_name archive_name
|
|
|
|
complete -o filenames -F _comp_archive_names archive_prune
|