Making my own contribution, these scripts are inspired from this, this and this scripts.
Script 1: Tree text backup of a folder
This one is quite simple, it just makes a tree backup of a designated folder.
#!/bin/bash
#name=backup-folder-as-tree
#description=Make a textual backup of a folder. Edit the section "USER PARAMETERS" to configure it.
#foregroundOnly=false
#backgroundOnly=false
#arrayStarted=true
#clearLog=false
#noParity=true
# ==============================================================================
# USER PARAMETERS
# Folder pathname to backup as tree, MUST be an absolute path.
# Example:
# SOURCE_PATH="/mnt/user/media/data"
SOURCE_PATH="/mnt/user/media/data"
# Folder pathname to store the output of tree, MUST be an absolute path.
# Example:
# LOG_PATH="/boot/config/index_tree/media.data"
LOG_PATH="/mnt/user/temp/test"
# Report name (no extension/suffix).
# Example:
# LOG_NAME="mnt.user.media"
LOG_NAME="mnt.user.media"
# ==============================================================================
# SCRIPT
function main() {
# Preparation
local timestamp=$(date +%Y%m%dT%H%M%S)
local logfilepath="$LOG_PATH/$timestamp-$LOG_NAME-tree.log"
mkdir -p "$LOG_PATH"
# Scan folder
echo "Scanning '$SOURCE_PATH'"
tree -aDFs --metafirst --timefmt "%Y-%m-%dT%H:%M:%S%z" -o "$logfilepath" "$SOURCE_PATH"
if [ -f "$logfilepath" ]; then
echo "Report created: '$logfilepath'"
else
echo "Could not create report: '$logfilepath'"
fi
}
# ==============================================================================
# MAIN
echo ""
echo "Starting script..."
main
echo "Done!"
Script 2: Selective tree text backup of one or several share(s) on any drive with rotative logs
This one does the same as above in a more flexible way: it'll backup one or multiple share(s) on any selected drive, pool or /mnt/user. Note that you have to know a little bit of regexp to configure it but examples should be enough. For instance, let's say I have the following folder structure:
/mnt
├── cache
│ ├── movies
│ ├── temp
│ └── tvshows
├── disk1
│ └── tvshows
├── disk2
│ ├── movies
│ └── temp
├── disk3
│ ├── movies
│ └── tvshows
├── disks
└── user
├── movies
├── temp
└── tvshows
If I want to make a backup of the movies/ and tvshows/ shares from all numbered disks, user and cache then I would need to provide:
DISK_REGEX="disk[0-9]+|cache|user"
SHARE_REGEX="movies|tvshows"
Another feature is rotative logs: it'll keep whatever number of logs you specify, deleting older logs. 0 or negative will keep all logs.
Here's the code:
#!/bin/bash
#name=backup-shares-as-tree
#description=Make a textual backup of one or several share(s) on /mnt/disk#, /mnt/cache and/or /mnt/user. Edit the section "USER PARAMETERS" to configure it.
#foregroundOnly=false
#backgroundOnly=false
#arrayStarted=true
#clearLog=false
#noParity=true
# ==============================================================================
# USER PARAMETERS
# Base location for disks, this will likely be /mnt, MUST be an absolute path
# Example:
# BASEDIR="/mnt"
BASEDIR="/mnt"
# Folder location to store the output of tree, MUST be an absolute path
# Example:
# LOG_PATH="/boot/config/index_tree"
LOG_PATH="/boot/config/index_tree"
# Maximum number of report files to keep per share. If you run this script daily
# and MAXLOGS=3, only the last 3 days will be kept. 0 or negative will keep all logs.
MAXLOGS=14
# POSIX-extended regex for "disks" (disk1, disk2, user, cache, etc.) and share
# names. The search will be applied on the following expression:
# "$BASEDIR/($DISK_REGEX)/($SHARE_REGEX)"
# Examples:
# DISK_REGEX="disk[0-9]+|cache|user"
# SHARE_REGEX="tvshows|movies|(share name with spaces)"
# SHARE_REGEX=".+" # Will match all your shares
DISK_REGEX="disk[0-9]+|cache|user"
SHARE_REGEX="media|data|temp"
# ==============================================================================
# SCRIPT
# Prevent empty result from glob to be interpreted litterally
shopt -s nullglob
# Better pathname processing, see https://dwheeler.com/essays/filenames-in-shell.html
IFS="$(printf '\n\t')"
set -euo pipefail
# Timestamp in log files
TIMESTAMP=$(date +%Y%m%dT%H%M%S)
#######################################
# Remove the oldest logs in a directory to keep only the $MAXLOGS most recent logs.
#
# Globals:
# MAXLOGS
# Arguments:
# Log directory, a path
#######################################
function remove_old_logs () {
# glob will list files by alphabetical order, so oldest first
# nullglob is important if no log is found
if [[ $1 == -* ]]; then
echo "Path '$1' starts with '-', exiting program."
exit 1
fi
if [[ $MAXLOGS -le 0 ]]; then
return 0
fi
local logfiles=("$1"/*.log)
local diff=$((${#logfiles[@]}-MAXLOGS))
for ((i=0; i<diff; i++)); do
rm "${logfiles[$i]}"
done
}
#######################################
# Scan a single share directory and log its tree to a log file.
#
# Options for tree:
# -a List all files.
# -F Append indicator (one of */=>@|) to entries.
# -D Print the date of last modification or (-c) status change.
# -s Print the size in bytes of each file.
# --metafirst Print meta-data at the beginning of each line.
# --timefmt <f> Print and format time according to the format <f>.
# In our case time is printed as per ISO 8601.
# -o Output to a file.
#
# Globals:
# LOG_PATH
# TIMESTAMP
# Arguments:
# Share to scan with the tree command, a path
#######################################
function scan_path () {
# Share info
if [[ $1 == -* ]]; then
echo "Path '$1' starts with '-', exiting program."
exit 1
fi
local sharename=$(basename "$sharepath")
# Disk info
local diskpath=$(dirname "$sharepath")
local diskname=$(basename "$diskpath")
# Log info
local logname="$TIMESTAMP-$diskname-$sharename-tree.log"
local logdirpath="$LOG_PATH/$diskname/$sharename"
local logfilepath="$logdirpath/$logname"
# Scanning
mkdir -p "$logdirpath"
echo "Scanning '$sharepath'"
tree -aDFs --metafirst --timefmt "%Y-%m-%dT%H:%M:%S%z" -o "$logfilepath" "$sharepath"
if [ -f "$logfilepath" ]; then
echo "Report created: '$logfilepath'"
else
echo "Could not create report: '$logfilepath'"
fi
remove_old_logs "$logdirpath"
}
#######################################
# Main thread
#
# Globals:
# BASEDIR
# DISK_REGEX
# SHARE_REGEX
# SHARE_PATHS
#######################################
function main() {
# Get share paths as array
readarray -d '' SHARE_PATHS < <(find "$BASEDIR" -maxdepth 2 -type d \
-regextype posix-extended -regex ".*($DISK_REGEX)/($SHARE_REGEX)" -print0)
# Scan shares
for sharepath in "${SHARE_PATHS[@]}"; do
local sharepath=$(realpath "$sharepath")
scan_path "$sharepath"
echo ""
done
}
# ==============================================================================
# MAIN
echo ""
echo "Starting script..."
main
echo "Done!"
backup-as-tree.zip