Skip to content

Commit 4090cc6

Browse files
authored
Merge pull request #19 from buildplan/dry-run
better dry-run and -R flag back with path adjustments
2 parents 11488ce + f61b4ad commit 4090cc6

File tree

3 files changed

+68
-24
lines changed

3 files changed

+68
-24
lines changed

README.md

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,12 @@ To run the backup automatically, edit the root crontab.
156156
157157
# --- Source and Destination ---
158158
# 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/"
159+
#
160+
# IMPORTANT: Follow these two rules for each path:
161+
# 1. End the path with a trailing slash (e.g., "user/").
162+
# 2. Use "/./" to mark the part of the path you want to create on the destination.
163+
# Example: "/home/./user/" will create a "user" directory in your BOX_DIR.
164+
BACKUP_DIRS="/home/./user/ /var/./log/ /etc/./nginx/"
161165
BOX_DIR="/home/myvps/"
162166
163167
# --- Connection Details ---
@@ -222,7 +226,7 @@ END_EXCLUDES
222226

223227
# =================================================================
224228
# SCRIPT INITIALIZATION & SETUP
225-
# v0.15 - 2025.08.10
229+
# v0.16 - 2025.08.10
226230
# =================================================================
227231
set -Euo pipefail
228232
umask 077
@@ -286,7 +290,7 @@ LOCK_FILE="/tmp/backup_rsync.lock"
286290
MAX_LOG_SIZE=10485760 # 10 MB in bytes
287291

288292
RSYNC_BASE_OPTS=(
289-
-a -z --delete --partial --timeout=60
293+
-aR -z --delete --partial --timeout=60
290294
--exclude-from="$EXCLUDE_FILE_TMP"
291295
-e "ssh ${SSH_OPTS_STR:-}"
292296
)
@@ -331,7 +335,7 @@ send_notification() {
331335
}
332336

333337
run_integrity_check() {
334-
local rsync_check_opts=(-ainc -c --delete --mkpath --exclude-from="$EXCLUDE_FILE_TMP" --out-format="%n" -e "ssh ${SSH_OPTS_STR:-}")
338+
local rsync_check_opts=(-aincR -c --delete --mkpath --exclude-from="$EXCLUDE_FILE_TMP" --out-format="%n" -e "ssh ${SSH_OPTS_STR:-}")
335339

336340
for dir in $BACKUP_DIRS; do
337341
local remote_path="${REMOTE_TARGET}${dir#/}"
@@ -441,22 +445,40 @@ fi
441445
if [[ "${1:-}" ]]; then
442446
trap - ERR
443447
case "${1}" in
448+
444449
--dry-run)
445450
trap - ERR
446451
echo "--- DRY RUN MODE ACTIVATED ---"
447452
DRY_RUN_FAILED=false
448-
for dir in $BACKUP_DIRS; do
449-
remote_path="${REMOTE_TARGET}${dir#/}"
450-
echo "--- Checking dry run for: $dir -> $remote_path"
451-
# shellcheck disable=SC2086
452-
if ! rsync "${RSYNC_BASE_OPTS[@]}" --dry-run --info=progress2 "$dir" "$remote_path"; then
453+
full_dry_run_output=""
454+
455+
read -ra DIRS_ARRAY <<< "$BACKUP_DIRS"
456+
for dir in "${DIRS_ARRAY[@]}"; do
457+
echo -e "\n--- Checking dry run for: $dir ---"
458+
459+
rsync_dry_opts=("${RSYNC_BASE_OPTS[@]}" --dry-run --itemize-changes --info=stats2)
460+
461+
DRY_RUN_LOG_TMP=$(mktemp)
462+
463+
if ! rsync "${rsync_dry_opts[@]}" "$dir" "$REMOTE_TARGET" > "$DRY_RUN_LOG_TMP" 2>&1; then
453464
DRY_RUN_FAILED=true
454465
fi
466+
467+
echo "---- Preview of changes ----"
468+
grep '^[*<>]' "$DRY_RUN_LOG_TMP" | head -n 20 || true
469+
echo "----------------------------"
470+
471+
full_dry_run_output+=$'\n'"$(<"$DRY_RUN_LOG_TMP")"
472+
rm -f "$DRY_RUN_LOG_TMP"
455473
done
456474

475+
echo -e "\n--- Overall Dry Run Summary ---"
476+
BACKUP_STATS=$(format_backup_stats "$full_dry_run_output")
477+
echo -e "$BACKUP_STATS"
478+
echo "-------------------------------"
479+
457480
if [[ "$DRY_RUN_FAILED" == "true" ]]; then
458-
echo ""
459-
echo "❌ Dry run FAILED for one or more directories. See the rsync error messages above for details."
481+
echo -e "\n❌ Dry run FAILED for one or more directories. See rsync errors above."
460482
exit 1
461483
fi
462484
echo "--- DRY RUN COMPLETED ---"; exit 0 ;;

backup.conf

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@
55

66
# --- Source and Destination ---
77
# 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/"
8+
#
9+
# IMPORTANT: Follow these two rules for each path:
10+
# 1. End the path with a trailing slash (e.g., "user/").
11+
# 2. Use "/./" to mark the part of the path you want to create on the destination.
12+
# Example: "/home/./user/" will create a "user" directory in your BOX_DIR.
13+
BACKUP_DIRS="/home/./user/ /var/./log/ /etc/./nginx/"
1014
BOX_DIR="/home/myvps/"
1115

1216
# --- Connection Details ---

backup_script.sh

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

33
# =================================================================
44
# SCRIPT INITIALIZATION & SETUP
5-
# v0.15 - 2025.08.10
5+
# v0.16 - 2025.08.10
66
# =================================================================
77
set -Euo pipefail
88
umask 077
@@ -66,7 +66,7 @@ LOCK_FILE="/tmp/backup_rsync.lock"
6666
MAX_LOG_SIZE=10485760 # 10 MB in bytes
6767

6868
RSYNC_BASE_OPTS=(
69-
-a -z --delete --partial --timeout=60
69+
-aR -z --delete --partial --timeout=60
7070
--exclude-from="$EXCLUDE_FILE_TMP"
7171
-e "ssh ${SSH_OPTS_STR:-}"
7272
)
@@ -111,7 +111,7 @@ send_notification() {
111111
}
112112

113113
run_integrity_check() {
114-
local rsync_check_opts=(-ainc -c --delete --mkpath --exclude-from="$EXCLUDE_FILE_TMP" --out-format="%n" -e "ssh ${SSH_OPTS_STR:-}")
114+
local rsync_check_opts=(-aincR -c --delete --mkpath --exclude-from="$EXCLUDE_FILE_TMP" --out-format="%n" -e "ssh ${SSH_OPTS_STR:-}")
115115

116116
for dir in $BACKUP_DIRS; do
117117
local remote_path="${REMOTE_TARGET}${dir#/}"
@@ -221,22 +221,40 @@ fi
221221
if [[ "${1:-}" ]]; then
222222
trap - ERR
223223
case "${1}" in
224+
224225
--dry-run)
225226
trap - ERR
226227
echo "--- DRY RUN MODE ACTIVATED ---"
227228
DRY_RUN_FAILED=false
228-
for dir in $BACKUP_DIRS; do
229-
remote_path="${REMOTE_TARGET}${dir#/}"
230-
echo "--- Checking dry run for: $dir -> $remote_path"
231-
# shellcheck disable=SC2086
232-
if ! rsync "${RSYNC_BASE_OPTS[@]}" --dry-run --info=progress2 "$dir" "$remote_path"; then
229+
full_dry_run_output=""
230+
231+
read -ra DIRS_ARRAY <<< "$BACKUP_DIRS"
232+
for dir in "${DIRS_ARRAY[@]}"; do
233+
echo -e "\n--- Checking dry run for: $dir ---"
234+
235+
rsync_dry_opts=("${RSYNC_BASE_OPTS[@]}" --dry-run --itemize-changes --info=stats2)
236+
237+
DRY_RUN_LOG_TMP=$(mktemp)
238+
239+
if ! rsync "${rsync_dry_opts[@]}" "$dir" "$REMOTE_TARGET" > "$DRY_RUN_LOG_TMP" 2>&1; then
233240
DRY_RUN_FAILED=true
234241
fi
242+
243+
echo "---- Preview of changes ----"
244+
grep '^[*<>]' "$DRY_RUN_LOG_TMP" | head -n 20 || true
245+
echo "----------------------------"
246+
247+
full_dry_run_output+=$'\n'"$(<"$DRY_RUN_LOG_TMP")"
248+
rm -f "$DRY_RUN_LOG_TMP"
235249
done
236250

251+
echo -e "\n--- Overall Dry Run Summary ---"
252+
BACKUP_STATS=$(format_backup_stats "$full_dry_run_output")
253+
echo -e "$BACKUP_STATS"
254+
echo "-------------------------------"
255+
237256
if [[ "$DRY_RUN_FAILED" == "true" ]]; then
238-
echo ""
239-
echo "❌ Dry run FAILED for one or more directories. See the rsync error messages above for details."
257+
echo -e "\n❌ Dry run FAILED for one or more directories. See rsync errors above."
240258
exit 1
241259
fi
242260
echo "--- DRY RUN COMPLETED ---"; exit 0 ;;

0 commit comments

Comments
 (0)