Skip to content

Instantly share code, notes, and snippets.

@isurfer21
Created November 30, 2025 07:15
Show Gist options
  • Select an option

  • Save isurfer21/049fd3b31b860f4c46cec5bd7db5f7aa to your computer and use it in GitHub Desktop.

Select an option

Save isurfer21/049fd3b31b860f4c46cec5bd7db5f7aa to your computer and use it in GitHub Desktop.
Shell script to synchronize source directory to target directory on external hard disk
#!/bin/zsh
# Script to synchronize source directory to target directory on external hard disk
# Default: one-way sync without deleting files on target.
# Options:
# -d, --dry → Simulate deletions (dry-run with --delete)
# -x, --prune → Perform actual deletions (--delete)
# -h, --help → Show help menu
show_help() {
echo "Usage: $1 [options] <source_dir> <target_dir>"
echo
echo "Options:"
echo " -d, --dry Simulate deletions (dry-run with --delete)"
echo " -x, --prune Perform actual deletions (--delete)"
echo " -h, --help Show this help menu"
echo
echo "Default behavior (no option):"
echo " Synchronize source to target without deleting extra files on target."
echo
echo "Examples:"
echo " $1 ~/data /mnt/external/data"
echo " $1 -d ~/data /mnt/external/data"
echo " $1 --prune ~/data /mnt/external/data"
}
# Parse options
mode=""
while [[ $# -gt 0 ]]; do
case "$1" in
-d|--dry)
mode="dry"
shift
;;
-x|--prune)
mode="prune"
shift
;;
-h|--help)
show_help ${0##*/}
exit 0
;;
*)
# Stop parsing options when first non-option is encountered
break
;;
esac
done
if [ $# -ne 2 ]; then
show_help ${0##*/}
exit 1
fi
source_dir="$1"
target_dir="$2"
# Check if source directory exists
if [ ! -d "$source_dir" ]; then
echo "Error: Source directory '$source_dir' does not exist."
exit 1
fi
# Check if target directory exists (create if not, but warn)
if [ ! -d "$target_dir" ]; then
echo "Warning: Target directory '$target_dir' does not exist. Creating it."
mkdir -p "$target_dir"
fi
# Base rsync options as array (prevents invalid option errors)
RSYNC_OPTS=(-av --progress --exclude=node_modules/ --exclude=.DS_Store)
# Execute based on mode
if [ -z "$mode" ]; then
echo "\nSynchronizing '$source_dir' to '$target_dir' (without deletions)..."
rsync "${RSYNC_OPTS[@]}" "$source_dir/" "$target_dir/"
elif [ "$mode" = "dry" ]; then
echo "\nSimulating deletions (--delete --dry-run)..."
rsync "${RSYNC_OPTS[@]}" --delete --dry-run "$source_dir/" "$target_dir/" | grep 'deleting'
elif [ "$mode" = "prune" ]; then
echo "\nPerforming deletions (--delete)..."
read "confirm?Are you sure you want to delete extra files on target? [y/N] "
if [[ "$confirm" =~ ^[Yy]$ ]]; then
rsync "${RSYNC_OPTS[@]}" --delete "$source_dir/" "$target_dir/"
else
echo "Deletion aborted."
fi
fi
echo "\nDone!"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment