Skip to content

Commit fc8dfa0

Browse files
authored
Merge pull request #11 from buildplan/add_multiple_directories
Add multiple directories
2 parents 1ee5df2 + ac69fd8 commit fc8dfa0

File tree

3 files changed

+80
-68
lines changed

3 files changed

+80
-68
lines changed

README.md

Lines changed: 40 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,9 @@ To run the backup automatically, edit the root crontab.
155155
# !! IMPORTANT !! Set file permissions to 600 (chmod 600 backup.conf)
156156
157157
# --- Source and Destination ---
158-
# IMPORTANT: LOCAL_DIR must end with a trailing slash!
159-
LOCAL_DIR="/home/user/"
158+
# List all source directories to back up, separated by spaces.
159+
# IMPORTANT: Each path MUST end with a trailing slash!
160+
BACKUP_DIRS="/home/user/ /usr/scripts/ /etc/apps/"
160161
BOX_DIR="/home/myvps/"
161162
162163
# --- Connection Details ---
@@ -221,7 +222,7 @@ END_EXCLUDES
221222

222223
# =================================================================
223224
# SCRIPT INITIALIZATION & SETUP
224-
# v0.8 - 2025.08.09
225+
# v0.9 - 2025.08.09
225226
# =================================================================
226227
set -Euo pipefail
227228
umask 077
@@ -266,7 +267,7 @@ else
266267
fi
267268

268269
# --- Validate that all required configuration variables are set ---
269-
for var in LOCAL_DIR BOX_DIR HETZNER_BOX SSH_OPTS_STR LOG_FILE \
270+
for var in BACKUP_DIRS BOX_DIR HETZNER_BOX SSH_OPTS_STR LOG_FILE \
270271
NTFY_PRIORITY_SUCCESS NTFY_PRIORITY_WARNING NTFY_PRIORITY_FAILURE \
271272
LOG_RETENTION_DAYS; do
272273
if [ -z "${!var:-}" ]; then
@@ -329,7 +330,8 @@ send_notification() {
329330

330331
run_integrity_check() {
331332
local rsync_check_opts=(-ainc -c --delete --exclude-from="$EXCLUDE_FILE_TMP" --out-format="%n" -e "ssh ${SSH_OPTS_STR:-}")
332-
LC_ALL=C rsync "${rsync_check_opts[@]}" "$LOCAL_DIR" "$REMOTE_TARGET" 2>> "${LOG_FILE:-/dev/null}"
333+
# shellcheck disable=SC2086
334+
LC_ALL=C rsync "${rsync_check_opts[@]}" $BACKUP_DIRS "$REMOTE_TARGET" 2>> "${LOG_FILE:-/dev/null}"
333335
}
334336

335337
parse_stat() {
@@ -389,10 +391,12 @@ if ! ssh ${SSH_OPTS_STR:-} -o BatchMode=yes -o ConnectTimeout=10 "$HETZNER_BOX"
389391
trap - ERR; exit 6
390392
fi
391393

392-
if [[ ! -d "$LOCAL_DIR" ]] || [[ "$LOCAL_DIR" != */ ]]; then
393-
send_notification "❌ Backup FAILED: ${HOSTNAME}" "x" "${NTFY_PRIORITY_FAILURE}" "failure" "FATAL: LOCAL_DIR must exist and end with a trailing slash ('/')."
394-
trap - ERR; exit 2
395-
fi
394+
for dir in $BACKUP_DIRS; do
395+
if [[ ! -d "$dir" ]] || [[ "$dir" != */ ]]; then
396+
send_notification "❌ Backup FAILED: ${HOSTNAME}" "x" "${NTFY_PRIORITY_FAILURE}" "failure" "FATAL: A directory in BACKUP_DIRS ('$dir') must exist and end with a trailing slash ('/')."
397+
trap - ERR; exit 2
398+
fi
399+
done
396400

397401

398402
# =================================================================
@@ -411,37 +415,38 @@ if [[ "${1:-}" ]]; then
411415
--dry-run)
412416
trap - ERR
413417
echo "--- DRY RUN MODE ACTIVATED ---"
414-
if ! rsync "${RSYNC_BASE_OPTS[@]}" --dry-run --info=progress2 "$LOCAL_DIR" "$REMOTE_TARGET"; then
418+
# shellcheck disable=SC2086
419+
if ! rsync "${RSYNC_BASE_OPTS[@]}" --dry-run --info=progress2 $BACKUP_DIRS "$REMOTE_TARGET"; then
415420
echo ""
416421
echo "❌ Dry run FAILED. See the rsync error message above for details."
417422
exit 1
418423
fi
419424
echo "--- DRY RUN COMPLETED ---"; exit 0 ;;
420-
--checksum)
425+
--checksum | --summary)
426+
# Both modes use the same check, just with different reporting
421427
echo "--- INTEGRITY CHECK MODE ACTIVATED ---"
422-
echo "Comparing checksums... this may take a while."
428+
echo "Calculating differences..."
423429
FILE_DISCREPANCIES=$(run_integrity_check)
424-
if [ -z "$FILE_DISCREPANCIES" ]; then
425-
echo "✅ Checksum validation passed. No discrepancies found."
426-
log_message "Checksum validation passed. No discrepancies found."
427-
send_notification "✅ Backup Integrity OK: ${HOSTNAME}" "white_check_mark" "${NTFY_PRIORITY_SUCCESS}" "success" "Checksum validation passed. No discrepancies found."
428-
else
429-
log_message "Backup integrity check FAILED. Found discrepancies."
430-
ISSUE_LIST=$(echo "${FILE_DISCREPANCIES}" | head -n 10)
431-
printf "❌ Backup integrity check FAILED. First 10 differing files:\n%s\n" "${ISSUE_LIST}"
432-
printf -v FAILURE_MSG "Backup integrity check FAILED.\n\nFirst 10 differing files:\n%s\n\nCheck log for full details." "${ISSUE_LIST}"
433-
send_notification "❌ Backup Integrity FAILED: ${HOSTNAME}" "x" "${NTFY_PRIORITY_FAILURE}" "failure" "${FAILURE_MSG}"
430+
431+
if [[ "$1" == "--summary" ]]; then
432+
MISMATCH_COUNT=$(echo "$FILE_DISCREPANCIES" | wc -l)
433+
printf "🚨 Total files with checksum mismatches: %d\n" "$MISMATCH_COUNT"
434+
log_message "Summary mode check found $MISMATCH_COUNT mismatched files."
435+
send_notification "📊 Backup Summary: ${HOSTNAME}" "bar_chart" "${NTFY_PRIORITY_SUCCESS}" "success" "Mismatched files found: $MISMATCH_COUNT"
436+
else # --checksum
437+
if [ -z "$FILE_DISCREPANCIES" ]; then
438+
echo "✅ Checksum validation passed. No discrepancies found."
439+
log_message "Checksum validation passed. No discrepancies found."
440+
send_notification "✅ Backup Integrity OK: ${HOSTNAME}" "white_check_mark" "${NTFY_PRIORITY_SUCCESS}" "success" "Checksum validation passed. No discrepancies found."
441+
else
442+
log_message "Backup integrity check FAILED. Found discrepancies."
443+
ISSUE_LIST=$(echo "${FILE_DISCREPANCIES}" | head -n 10)
444+
printf "❌ Backup integrity check FAILED. First 10 differing files:\n%s\n" "${ISSUE_LIST}"
445+
printf -v FAILURE_MSG "Backup integrity check FAILED.\n\nFirst 10 differing files:\n%s\n\nCheck log for full details." "${ISSUE_LIST}"
446+
send_notification "❌ Backup Integrity FAILED: ${HOSTNAME}" "x" "${NTFY_PRIORITY_FAILURE}" "failure" "${FAILURE_MSG}"
447+
fi
434448
fi
435449
exit 0 ;;
436-
437-
--summary)
438-
echo "--- INTEGRITY SUMMARY MODE ---"
439-
echo "Calculating differences..."
440-
MISMATCH_COUNT=$(run_integrity_check | wc -l)
441-
printf "🚨 Total files with checksum mismatches: %d\n" "$MISMATCH_COUNT"
442-
log_message "Summary mode check found $MISMATCH_COUNT mismatched files."
443-
send_notification "📊 Backup Summary: ${HOSTNAME}" "bar_chart" "${NTFY_PRIORITY_SUCCESS}" "success" "Mismatched files found: $MISMATCH_COUNT"
444-
exit 0 ;;
445450
esac
446451
fi
447452

@@ -451,7 +456,6 @@ flock -n 200 || { echo "Another instance is running, exiting."; exit 5; }
451456
if [ -f "$LOG_FILE" ] && [ "$(stat -c%s "$LOG_FILE")" -gt "$MAX_LOG_SIZE" ]; then
452457
mv "$LOG_FILE" "${LOG_FILE}.$(date +%Y%m%d_%H%M%S)"
453458
touch "$LOG_FILE"
454-
# Clean up old log files
455459
find "$(dirname "$LOG_FILE")" -name "$(basename "$LOG_FILE").*" -type f -mtime +"$LOG_RETENTION_DAYS" -delete
456460
fi
457461

@@ -466,11 +470,13 @@ RSYNC_OPTS=("${RSYNC_BASE_OPTS[@]}")
466470

467471
if [[ "$VERBOSE_MODE" == "true" ]]; then
468472
RSYNC_OPTS+=(--info=stats2,progress2)
469-
nice -n 19 ionice -c 3 rsync "${RSYNC_OPTS[@]}" "$LOCAL_DIR" "$REMOTE_TARGET" 2>&1 | tee "$RSYNC_LOG_TMP"
473+
# shellcheck disable=SC2086
474+
nice -n 19 ionice -c 3 rsync "${RSYNC_OPTS[@]}" $BACKUP_DIRS "$REMOTE_TARGET" 2>&1 | tee "$RSYNC_LOG_TMP"
470475
RSYNC_EXIT_CODE=${PIPESTATUS[0]}
471476
else
472477
RSYNC_OPTS+=(--info=stats2)
473-
nice -n 19 ionice -c 3 rsync "${RSYNC_OPTS[@]}" "$LOCAL_DIR" "$REMOTE_TARGET" > "$RSYNC_LOG_TMP" 2>&1 || RSYNC_EXIT_CODE=$?
478+
# shellcheck disable=SC2086
479+
nice -n 19 ionice -c 3 rsync "${RSYNC_OPTS[@]}" $BACKUP_DIRS "$REMOTE_TARGET" > "$RSYNC_LOG_TMP" 2>&1 || RSYNC_EXIT_CODE=$?
474480
fi
475481

476482
END_TIME=$(date +%s)

backup.conf

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
# !! IMPORTANT !! Set file permissions to 600 (chmod 600 backup.conf)
55

66
# --- Source and Destination ---
7-
# IMPORTANT: LOCAL_DIR must end with a trailing slash!
8-
LOCAL_DIR="/home/user/"
7+
# List all source directories to back up, separated by spaces.
8+
# IMPORTANT: Each path MUST end with a trailing slash!
9+
BACKUP_DIRS="/home/user/ /usr/scripts/ /etc/apps/"
910
BOX_DIR="/home/myvps/"
1011

1112
# --- Connection Details ---

backup_script.sh

Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
# =================================================================
44
# SCRIPT INITIALIZATION & SETUP
5-
# v0.8 - 2025.08.09
5+
# v0.9 - 2025.08.09
66
# =================================================================
77
set -Euo pipefail
88
umask 077
@@ -47,7 +47,7 @@ else
4747
fi
4848

4949
# --- Validate that all required configuration variables are set ---
50-
for var in LOCAL_DIR BOX_DIR HETZNER_BOX SSH_OPTS_STR LOG_FILE \
50+
for var in BACKUP_DIRS BOX_DIR HETZNER_BOX SSH_OPTS_STR LOG_FILE \
5151
NTFY_PRIORITY_SUCCESS NTFY_PRIORITY_WARNING NTFY_PRIORITY_FAILURE \
5252
LOG_RETENTION_DAYS; do
5353
if [ -z "${!var:-}" ]; then
@@ -110,7 +110,8 @@ send_notification() {
110110

111111
run_integrity_check() {
112112
local rsync_check_opts=(-ainc -c --delete --exclude-from="$EXCLUDE_FILE_TMP" --out-format="%n" -e "ssh ${SSH_OPTS_STR:-}")
113-
LC_ALL=C rsync "${rsync_check_opts[@]}" "$LOCAL_DIR" "$REMOTE_TARGET" 2>> "${LOG_FILE:-/dev/null}"
113+
# shellcheck disable=SC2086
114+
LC_ALL=C rsync "${rsync_check_opts[@]}" $BACKUP_DIRS "$REMOTE_TARGET" 2>> "${LOG_FILE:-/dev/null}"
114115
}
115116

116117
parse_stat() {
@@ -170,10 +171,12 @@ if ! ssh ${SSH_OPTS_STR:-} -o BatchMode=yes -o ConnectTimeout=10 "$HETZNER_BOX"
170171
trap - ERR; exit 6
171172
fi
172173

173-
if [[ ! -d "$LOCAL_DIR" ]] || [[ "$LOCAL_DIR" != */ ]]; then
174-
send_notification "❌ Backup FAILED: ${HOSTNAME}" "x" "${NTFY_PRIORITY_FAILURE}" "failure" "FATAL: LOCAL_DIR must exist and end with a trailing slash ('/')."
175-
trap - ERR; exit 2
176-
fi
174+
for dir in $BACKUP_DIRS; do
175+
if [[ ! -d "$dir" ]] || [[ "$dir" != */ ]]; then
176+
send_notification "❌ Backup FAILED: ${HOSTNAME}" "x" "${NTFY_PRIORITY_FAILURE}" "failure" "FATAL: A directory in BACKUP_DIRS ('$dir') must exist and end with a trailing slash ('/')."
177+
trap - ERR; exit 2
178+
fi
179+
done
177180

178181

179182
# =================================================================
@@ -192,37 +195,38 @@ if [[ "${1:-}" ]]; then
192195
--dry-run)
193196
trap - ERR
194197
echo "--- DRY RUN MODE ACTIVATED ---"
195-
if ! rsync "${RSYNC_BASE_OPTS[@]}" --dry-run --info=progress2 "$LOCAL_DIR" "$REMOTE_TARGET"; then
198+
# shellcheck disable=SC2086
199+
if ! rsync "${RSYNC_BASE_OPTS[@]}" --dry-run --info=progress2 $BACKUP_DIRS "$REMOTE_TARGET"; then
196200
echo ""
197201
echo "❌ Dry run FAILED. See the rsync error message above for details."
198202
exit 1
199203
fi
200204
echo "--- DRY RUN COMPLETED ---"; exit 0 ;;
201-
--checksum)
205+
--checksum | --summary)
206+
# Both modes use the same check, just with different reporting
202207
echo "--- INTEGRITY CHECK MODE ACTIVATED ---"
203-
echo "Comparing checksums... this may take a while."
208+
echo "Calculating differences..."
204209
FILE_DISCREPANCIES=$(run_integrity_check)
205-
if [ -z "$FILE_DISCREPANCIES" ]; then
206-
echo "✅ Checksum validation passed. No discrepancies found."
207-
log_message "Checksum validation passed. No discrepancies found."
208-
send_notification "✅ Backup Integrity OK: ${HOSTNAME}" "white_check_mark" "${NTFY_PRIORITY_SUCCESS}" "success" "Checksum validation passed. No discrepancies found."
209-
else
210-
log_message "Backup integrity check FAILED. Found discrepancies."
211-
ISSUE_LIST=$(echo "${FILE_DISCREPANCIES}" | head -n 10)
212-
printf "❌ Backup integrity check FAILED. First 10 differing files:\n%s\n" "${ISSUE_LIST}"
213-
printf -v FAILURE_MSG "Backup integrity check FAILED.\n\nFirst 10 differing files:\n%s\n\nCheck log for full details." "${ISSUE_LIST}"
214-
send_notification "❌ Backup Integrity FAILED: ${HOSTNAME}" "x" "${NTFY_PRIORITY_FAILURE}" "failure" "${FAILURE_MSG}"
210+
211+
if [[ "$1" == "--summary" ]]; then
212+
MISMATCH_COUNT=$(echo "$FILE_DISCREPANCIES" | wc -l)
213+
printf "🚨 Total files with checksum mismatches: %d\n" "$MISMATCH_COUNT"
214+
log_message "Summary mode check found $MISMATCH_COUNT mismatched files."
215+
send_notification "📊 Backup Summary: ${HOSTNAME}" "bar_chart" "${NTFY_PRIORITY_SUCCESS}" "success" "Mismatched files found: $MISMATCH_COUNT"
216+
else # --checksum
217+
if [ -z "$FILE_DISCREPANCIES" ]; then
218+
echo "✅ Checksum validation passed. No discrepancies found."
219+
log_message "Checksum validation passed. No discrepancies found."
220+
send_notification "✅ Backup Integrity OK: ${HOSTNAME}" "white_check_mark" "${NTFY_PRIORITY_SUCCESS}" "success" "Checksum validation passed. No discrepancies found."
221+
else
222+
log_message "Backup integrity check FAILED. Found discrepancies."
223+
ISSUE_LIST=$(echo "${FILE_DISCREPANCIES}" | head -n 10)
224+
printf "❌ Backup integrity check FAILED. First 10 differing files:\n%s\n" "${ISSUE_LIST}"
225+
printf -v FAILURE_MSG "Backup integrity check FAILED.\n\nFirst 10 differing files:\n%s\n\nCheck log for full details." "${ISSUE_LIST}"
226+
send_notification "❌ Backup Integrity FAILED: ${HOSTNAME}" "x" "${NTFY_PRIORITY_FAILURE}" "failure" "${FAILURE_MSG}"
227+
fi
215228
fi
216229
exit 0 ;;
217-
218-
--summary)
219-
echo "--- INTEGRITY SUMMARY MODE ---"
220-
echo "Calculating differences..."
221-
MISMATCH_COUNT=$(run_integrity_check | wc -l)
222-
printf "🚨 Total files with checksum mismatches: %d\n" "$MISMATCH_COUNT"
223-
log_message "Summary mode check found $MISMATCH_COUNT mismatched files."
224-
send_notification "📊 Backup Summary: ${HOSTNAME}" "bar_chart" "${NTFY_PRIORITY_SUCCESS}" "success" "Mismatched files found: $MISMATCH_COUNT"
225-
exit 0 ;;
226230
esac
227231
fi
228232

@@ -232,7 +236,6 @@ flock -n 200 || { echo "Another instance is running, exiting."; exit 5; }
232236
if [ -f "$LOG_FILE" ] && [ "$(stat -c%s "$LOG_FILE")" -gt "$MAX_LOG_SIZE" ]; then
233237
mv "$LOG_FILE" "${LOG_FILE}.$(date +%Y%m%d_%H%M%S)"
234238
touch "$LOG_FILE"
235-
# Clean up old log files
236239
find "$(dirname "$LOG_FILE")" -name "$(basename "$LOG_FILE").*" -type f -mtime +"$LOG_RETENTION_DAYS" -delete
237240
fi
238241

@@ -247,11 +250,13 @@ RSYNC_OPTS=("${RSYNC_BASE_OPTS[@]}")
247250

248251
if [[ "$VERBOSE_MODE" == "true" ]]; then
249252
RSYNC_OPTS+=(--info=stats2,progress2)
250-
nice -n 19 ionice -c 3 rsync "${RSYNC_OPTS[@]}" "$LOCAL_DIR" "$REMOTE_TARGET" 2>&1 | tee "$RSYNC_LOG_TMP"
253+
# shellcheck disable=SC2086
254+
nice -n 19 ionice -c 3 rsync "${RSYNC_OPTS[@]}" $BACKUP_DIRS "$REMOTE_TARGET" 2>&1 | tee "$RSYNC_LOG_TMP"
251255
RSYNC_EXIT_CODE=${PIPESTATUS[0]}
252256
else
253257
RSYNC_OPTS+=(--info=stats2)
254-
nice -n 19 ionice -c 3 rsync "${RSYNC_OPTS[@]}" "$LOCAL_DIR" "$REMOTE_TARGET" > "$RSYNC_LOG_TMP" 2>&1 || RSYNC_EXIT_CODE=$?
258+
# shellcheck disable=SC2086
259+
nice -n 19 ionice -c 3 rsync "${RSYNC_OPTS[@]}" $BACKUP_DIRS "$REMOTE_TARGET" > "$RSYNC_LOG_TMP" 2>&1 || RSYNC_EXIT_CODE=$?
255260
fi
256261

257262
END_TIME=$(date +%s)

0 commit comments

Comments
 (0)