Skip to content

Instantly share code, notes, and snippets.

@Atrate
Created September 10, 2023 20:15
Show Gist options
  • Save Atrate/533f506a8d3552702b933a5aa18a854a to your computer and use it in GitHub Desktop.
Save Atrate/533f506a8d3552702b933a5aa18a854a to your computer and use it in GitHub Desktop.
Restore files that were backed up by Syncthing as "sync conflicts"
#!/bin/bash --posix
# ------------------------------------------------------------------------
# Copyright (C) 2023 Atrate
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# ------------------------------------------------------------------------
# --------------
# Version: 0.1.0
# --------------
# ------------------------------------------------------------------------------
# Simple script that restores files marked as "sync conflict" by syncthing based
# on the timestamp (in days only)
# ------------------
# Exit code listing:
# 0: All good
# 1: Unspecified
# 2: Error in environment configuration or arguments
# ------------------------------------------------------------------------------
# Print script usage
# ------------------
print_usage()
{
cat << USAGE >&2
Usage: $(basename "$0"): [OPTIONS] TIMESTAMP
Recursively restore files in the current working directory from a Syncthing
sync-conflict backup.
Options:
-h, --help print this usage information
-n, --dry-run show what would be done, don't perform any other actions
Examples:
$(basename "$0") 20230902
The TIMESTAMP argument needs to be in the format YYYYMMDD.
USAGE
return 0
}
# Parse commandline options
# Adapted from https://stackoverflow.com/a/29754866
# -------------------------------------------------
parse_options()
{
# Check whether getopt version is "enhanced"
# shellcheck disable=SC2251
# ------------------------------------------
! getopt --test >&3
if [[ ${PIPESTATUS[0]} -ne 4 ]]
then
err 'This script requres getopt enhanced to work properly'
exit 2
fi
# Set available options
# ---------------------
LONGOPTS=dry-run,help
# LONGOPTS+=,config:
OPTIONS=hn
# OPTIONS+=c:
# Get arguments from "$@" using getopt
# ------------------------------------
IFS=" " read -r -a PARSED <<< "$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@")"
eval set -- "${PARSED[*]}"
# Declare argument variables' default values
# ------------------------------------------
declare -g DRYRUN="false"
# Write to argument variables
# ---------------------------
while true
do
case "$1" in
-h|--help)
print_usage
exit 0
;;
-n|--dry-run)
DRYRUN="true"
shift
;;
--)
shift
break
;;
*)
echo "Invalid argument"
print_usage
exit 2
;;
esac
done
# Handle positional arguments ($1, $2, …)
# ---------------------------------------
if [[ $# -ne 1 ]]
then
echo "$(basename "$0"): TIMESTAMP required but not provided"
print_usage
exit 2
else
# Check whether $1 is a correct TIMESTAMP
# ---------------------------------------
if echo "$1" | grep -qE '^[0-9]{8}$'
then
declare -g TIMESTAMP
TIMESTAMP=$1
else
echo "$(basename "$0"): TIMESTAMP needs to be in the format YYYYMMDD"
exit 2
fi
fi
return
}
# Main program functionality
# --------------------------
main()
{
while read -r file
do
# Skip empty lines/empty input
# ----------------------------
[ -z "$file" ] && continue
# Extract the original filename
# -----------------------------
local target
target="$(echo "$file" \
| sed -E "s/\.sync-conflict-$TIMESTAMP-[0-9]*-[0-9A-Z]*//")"
if [ "$DRYRUN" = "true" ]
then
# Echo what would be done
# -----------------------
echo cp -vaf -- "$file" "$target"
else
# Copy the backed-up file over the 'original'
# -------------------------------------------
cp -vaf -- "$file" "$target"
fi
done <<< "$(find . -type f -iname "*sync-conflict-$TIMESTAMP*")"
}
parse_options "$@"
main "$@"
## END OF FILE #################################################################
# vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4 smarttab:
# End
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment