Skip to content

Instantly share code, notes, and snippets.

@bumbummen99
Last active May 18, 2025 20:52
Show Gist options
  • Save bumbummen99/fdef28a91ec1cf29569b07b7e837af50 to your computer and use it in GitHub Desktop.
Save bumbummen99/fdef28a91ec1cf29569b07b7e837af50 to your computer and use it in GitHub Desktop.
Small rsync script to copy media from a library to another library
#!/usr/bin/env bash
# Configure common information
LIBRARIES=("Filme" "Serien" "Musik")
#########################
# Argument: Destination #
#########################
# Ceck if there is an argument
if [ -n "$1" ]; then
# Use the argument as the destination
DESTINATION="$1"
else
# Read the destination from user input
read -ep "Please enther the destination library's path: " DESTINATION
fi
###################################
# Optional Argument: Sources list #
###################################
SOURCES_LIST=""
if [ -n "$2" ]; then
if [ -f "$2" ]; then
SOURCES_LIST="$2"
else
echo "The provided list '$2' does not exist or is not a file."
exit 1
fi
fi
###########
# Sources #
###########
# Inform the user on how to enter sources
echo "Enter the individual paths you want to synchronize to the destination library ($DESTINATION)."
echo "Use ENTER to confirm the entered path. Enter an empty path to begin the synchronization."
# Collect the sources provided by user input
SOURCES=()
if [ -n "$SOURCES_LIST" ]; then
# Read paths from file
while IFS= read -r LINE || [ -n "$LINE" ]; do
# Skip empty lines or lines starting with #
[[ -z "$LINE" || "$LINE" =~ ^# ]] && continue
if [ ! -e "$LINE" ]; then
echo "Warning: The path in file does not exist: $LINE"
else
SOURCES+=("$LINE")
fi
done < "$SOURCES_LIST"
else
while true; do
read -ep "Path: " SOURCE
if [ -z "$SOURCE" ]; then
break
fi
if [ ! -e "$SOURCE" ]; then
echo "The provided path does not exist: $SOURCE"
else
SOURCES+=("$SOURCE")
fi
done
fi
# Ensure that any sources have been provided
if [ ${#SOURCES[@]} -eq 0 ]; then
echo "No paths have been provided, exiting."
exit 1
fi
##########################
# Common relative parent #
##########################
# Create a temporary directory without leaving the filesystem
TMP_DIR=$(mktemp -d)
# Link the provided sources to the temporary directory
for SOURCE in "${SOURCES[@]}"; do
for LIBRARY in "${LIBRARIES[@]}"; do
# Check if the source is within the current library
if [[ "$SOURCE" == *"/$LIBRARY/"* ]]; then
# Extract the relative path starting from the library (e.g., Filme/Alien/Alien 1)
RELATIVE_PATH="${SOURCE#*"/$LIBRARY/"}"
RELATIVE_PATH="$LIBRARY/$RELATIVE_PATH"
# Create the necessary directories in the temp directory
mkdir -p "$TMP_DIR/$(dirname "$RELATIVE_PATH")"
# Create a symbolic link to the source in the temp directory
ln -s "$SOURCE" "$TMP_DIR/$RELATIVE_PATH"
break
fi
done
done
###############
# Synchronize #
###############
# Initialize the rsync arguments list
ARGS=()
# Functionality
ARGS+=("--relative") # Use relative paths to keep path structure
ARGS+=("--recursive") # Sync provided sources recursively
ARGS+=("--no-inc-recursive") # Recurse before starting sync to prevent invalid / incrementing estimates in the output
ARGS+=("--copy-links") # Sync the symlinks in TMP_DIR as regular folders / files
ARGS+=("--times") # Set modified time to prevent unecessary writes in case nothing changed
# Configure the output
ARGS+=("--verbose") # Use verbose output
ARGS+=("--info=progress2") # Show progress of the sync operation
ARGS+=("--info=name0")
# Dry run rsync to show the changes to be made by the sync
rsync "${ARGS[@]}" --dry-run --itemize-changes "$TMP_DIR/" "$DESTINATION"
# Ask the user to confirm the changes
if read -rp "Do you want to proceed with the changes above? (y/n): " ANSWER && [[ "$ANSWER" =~ ^[Yy]$ ]]; then
# Run the final rsync command to sync
rsync "${ARGS[@]}" "$TMP_DIR/" "$DESTINATION"
else
echo "Cancelled synchronization."
fi
###########
# Cleanup #
###########
# Remove the temporary directory
rm -rf "$TMP_DIR"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment