Created
November 30, 2025 07:15
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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