Created
September 2, 2024 04:55
-
-
Save gdiaz384/ea57236d4af4e7d89b6b78a9ef67caad to your computer and use it in GitHub Desktop.
Bash reimplementation of robocopy ABI using rclone.
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
#!/usr/bin/env /bin/bash | |
# robocopy.sh is a Linux Bash script that mirrors directory trees using rsync using a somewhat similar ABI to the idea source. However, excluding directories is not supported. | |
# Installation: | |
# 1) Install rysnc: sudo apt-get install -y rsync | |
# 1) Download robocopy.sh | |
# 2) Place robocopy.sh somewhere in $PATH | |
# 3) Mark the script as executable: chmod +x robocopy.sh | |
# 4) (Optional) Rename robocopy.sh to robocopy if desired. | |
# 5) ./robocopy --help | |
# Documentation: | |
# https://devhints.io/bash | |
# https://unix.stackexchange.com/questions/203846/how-to-sync-two-folders-with-command-line-tools | |
# http://www.linuxfocus.org/~guido/scripts/recursive-file-copy-linux.html | |
# https://stackoverflow.com/questions/1728683/case-insensitive-comparison-of-strings-in-shell-script | |
# Syntax: | |
# Mirror the content of /source/mydirectory/ to /destination/directory/mydirectory | |
# cd /source | |
# rclone -v -c mydirectory /destination/directory | |
# Dry-run this by adding -n | |
# rclone -v -c -n mydirectory /destination/directory | |
# Documentation: | |
# -v means verbose | |
# -c means --checksum which means skip based on the checksum of the data, not modification-time and size. This will fully read both the source and destination files in order to compare them. | |
# -n means --dry-run which means perform a trial run with no changes made | |
# --transfers 1 limits the number of parallel transfers. For local transfers, 1 is ideal. The default value is 4. | |
# Alternative syntaxes using rsync and cp for copying copying from source to destination: | |
# rsync -v -u -a --del mydirectory/ /destination/directory # The trailing / is required for the source. | |
# cp -r -u -v "$sourceDirectory" "$targetDirectory" | |
# Case insensitive compare (simple case that requires multiple lines): | |
# var1=match | |
# var2=MATCH | |
# if echo $var1 | grep -i "^${var2}$" > /dev/null ; then | |
# echo "MATCH" | |
# fi | |
#set -euo pipefail # The -u option is not natively compatible with scripts that accept input. See: http://redsymbol.net/articles/unofficial-bash-strict-mode/ "Positional Parameters" | |
set -eo pipefail | |
IFS=$'\n\t' | |
#scriptPath=$(dirname "$(readlink -f "$BASH_SOURCE")") | |
scriptName=$(basename "$0") | |
usage() { | |
echo $scriptName mirrors directory trees using rclone. | |
echo | |
echo Description: | |
echo - Mirroring means all files in the target directory will be replaced by the source directory. Mirroring is different than copying the source directory into the target directory as a subfolder. | |
echo - If the targetDirectory does not exist, it will be created, including during test runs. | |
echo - During test runs, if the targetDirectory does not exist and the test run is successful, then the targetDirectory and only the targetDirectory is removed after printing the simulated actions. The parents of targetDirectory will remain. | |
echo Example: \"$scriptName test \~/a/b/c/d\" means that \~/a, \~/a/b, \~/a/b/c, and \~/a/b/c/d will be created but only \~/a/b/c/d will be removed after the test run completes. | |
echo - The printed paths show the paths after resolving them. | |
echo - Path resolution when using the relative to home symbol \~ can be flaky/wrong, so always triple check the printed paths when using a tilde \~. | |
echo | |
echo Dependencies: bash, rclone https://rclone.org/install/ | |
echo sudo apt-get install -y rclone | |
echo rclone --version | |
echo | |
echo Syntax: | |
echo $scriptName sourceDirectory targetDirectory [/mir] [/d] | |
echo - [ ] means optional. | |
echo - Omitting /mir means to do a test run for debugging. | |
echo - /d means to delete files in the destination that are not in the source. | |
echo - Omitting /d means keeping files at the destination even if they do not exist at the source. | |
echo - \"\" means to use the default option. | |
echo | |
echo Examples: | |
echo $scriptName test test2 | |
echo $scriptName test test2 /mir | |
echo $scriptName test test2 /mir /d | |
echo $scriptName test test2 \"\" /d | |
echo $scriptName /home/user/test test2 | |
echo $scriptName /home/user/test Downloads/test2 | |
echo $scriptName /home/user/test /Downloads/test2 | |
echo $scriptName /home/user/test \~/Downloads/test2 | |
echo $scriptName /home/user/test \~/Downloads/test2 \"\" /d | |
echo $scriptName /home/user/test \~/Downloads/test2 /mir | |
echo $scriptName /home/user/test \~/Downloads/test2 /mir /d | |
echo | |
} | |
if [[ "$1" == "" || "$2" == "" || "$1" == "-h" || "$1" == "-H" || "$1" == "--help" || "$1" == "--HELP" || "$1" == "/?" || "$1" == "?" || "$2" == "-h" || "$2" == "-H" || "$2" == "--help" || "$2" == "--HELP" || "$2" == "/?" || "$2" == "?" || "$3" == "-h" || "$3" == "-H" || "$3" == "--help" || "$3" == "--HELP" || "$3" == "/?" || "$3" == "?" ]]; then | |
usage | |
exit | |
fi | |
if [[ ! -d "$1" ]]; then | |
echo Error: "$1" folder does not exist. | |
exit | |
fi | |
targetIsTemporary=False | |
if [[ ! -d "$2" ]]; then | |
if [[ -f "$2" ]]; then | |
echo Error: "$2" already exists as a file. | |
exit | |
fi | |
echo Info: "$2" does not exist. | |
mkdir -p "$2" | |
# Permissions errors sometimes prevent this from working. | |
if [[ ! -d "$2" ]]; then | |
echo Error: "$2" does not exist | |
exit | |
fi | |
targetIsTemporary=True | |
fi | |
sourceDirectory="$(builtin cd "$1"; pwd)" | |
targetDirectory="$(builtin cd "$2"; pwd)" | |
homeDirectory=~ | |
#targetDirectory="$(dirname "$targetDirectory")" | |
echo Copying from: "$sourceDirectory" | |
echo to: "$targetDirectory" | |
#echo Copying "$1" to "$2" | |
#echo $sourceDirectory | |
#echo $targetDirectory | |
# Final check. | |
if [[ ! -d "$sourceDirectory" ]]; then | |
echo Error: "$sourceDirectory" does not exist | |
exit | |
fi | |
if [[ ! -d "$targetDirectory" ]]; then | |
echo Error: "$targetDirectory" does not exist. | |
exit | |
fi | |
# Copying over the home directory or root is probably a mistake, so do not allow it. | |
if [[ "$targetDirectory" == "$homeDirectory" || "$targetDirectory" == "/" ]]; then | |
echo Error: Unable to set target directory. | |
exit | |
fi | |
# Core logic. | |
if [[ "$3" != "/mir" ]]; then | |
echo Info: The following changes will occur inside of \"$targetDirectory\" | |
if [[ "$4" == "/d" || "$4" == "/D" || "$4" == "/del" ]]; then | |
rclone -v -c -n --transfers 1 sync "$sourceDirectory/" "$targetDirectory" | |
else | |
rclone -v -c -n --transfers 1 copy "$sourceDirectory/" "$targetDirectory" | |
fi | |
if [[ "$targetIsTemporary" == "True" ]]; then | |
rmdir "$2" | |
fi | |
elif [[ "$3" == "/mir" ]]; then | |
if [[ "$4" == "/d" || "$4" == "/D" || "$4" == "/del" ]]; then | |
rclone -v -c --transfers 1 sync "$sourceDirectory/" "$targetDirectory" | |
else | |
rclone -v -c --transfers 1 copy "$sourceDirectory/" "$targetDirectory" | |
fi | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment