130 lines
3.8 KiB
Text
130 lines
3.8 KiB
Text
|
#!/usr/bin/env bash
|
||
|
#
|
||
|
# Archives files and directories to a central depository via hardlinks
|
||
|
# Takes either a file or directory as argument and links them to the archive.
|
||
|
#
|
||
|
# Archiving does *not* duplicate the data, it just provides another permanent
|
||
|
# pointer to it, so the file will not be lost even if you delete it from the
|
||
|
# rest of your folders.
|
||
|
|
||
|
show_help() {
|
||
|
printf """
|
||
|
archive.sh: Hard linking your stuff.
|
||
|
|
||
|
archive.sh is not backup software, rather it will create hardlinks for your files.
|
||
|
What this accomplishes is multiple links into the same data on your hard drive,
|
||
|
so wherever you put the original file another pointer to it will reside in your archive
|
||
|
directory. You can think of it almost like the opposite of backups: You still only
|
||
|
have one copy of the file, but multiple places where you link to it.
|
||
|
This allows you to make the file in question the single source of truth in your system,
|
||
|
good for stopping duplicate downloading or contributing continued upload to
|
||
|
decentralized download protocols while still organizing your files.
|
||
|
|
||
|
Usage: archive.sh [OPTION] <file|directory>
|
||
|
|
||
|
Point it to a file or directory that you want linked.
|
||
|
|
||
|
Options:
|
||
|
|
||
|
-h Display this help.
|
||
|
|
||
|
-d Directory to archive to.
|
||
|
|
||
|
-f Force archive to link. By default, will skip file entries already
|
||
|
existing in archive and not link the current file at all.
|
||
|
This forces the archive to link the files. It will not overwrite
|
||
|
the already existing entry, instead creating a new unique archive
|
||
|
entry by prepending the current unix timestamp.
|
||
|
|
||
|
-a Absolute linking. By default, will rebuild the same directory
|
||
|
structure as in the original in the archive. Using this flag tells
|
||
|
archive.sh to instead link everything directly in the root of the
|
||
|
archive directory, creating a flat directory without any nesting.
|
||
|
Can create a more coherent archive, but runs the danger of
|
||
|
accidentally running into file conflicts.
|
||
|
|
||
|
-n Dryrun. Do not link anything yet but print the commands that would
|
||
|
be executed.
|
||
|
"""
|
||
|
}
|
||
|
|
||
|
while getopts "nahfd:" opt; do
|
||
|
case "$opt" in
|
||
|
# v) verbose=1
|
||
|
# ;;
|
||
|
f)
|
||
|
FORCE="true"
|
||
|
;;
|
||
|
a)
|
||
|
ABSOLUTE="true"
|
||
|
;;
|
||
|
d)
|
||
|
ARCHIVEDIR="$OPTARG"
|
||
|
;;
|
||
|
n)
|
||
|
DRYRUN="true"
|
||
|
;;
|
||
|
h | \? | *)
|
||
|
show_help
|
||
|
exit 0
|
||
|
;;
|
||
|
esac
|
||
|
done
|
||
|
shift $((OPTIND - 1))
|
||
|
|
||
|
main() {
|
||
|
archivedir="${ARCHIVEDIR:-/mnt/dietpi_userdata/minio-data/videos/archive}"
|
||
|
source="${1:-"."}"
|
||
|
_ensuredir "${archivedir}"
|
||
|
|
||
|
find "$source" -type f | while read -r file; do
|
||
|
if [ "$ABSOLUTE" = true ]; then
|
||
|
fname="$(basename "$file")"
|
||
|
else
|
||
|
fname="$file"
|
||
|
_ensuredir "${archivedir}/$(dirname "$fname")"
|
||
|
fi
|
||
|
|
||
|
ERROR=$(_link "$file" "${archivedir}/${fname}")
|
||
|
stat="$?"
|
||
|
|
||
|
#force mode: also add duplicates, but inform user
|
||
|
if [ "$FORCE" = "true" ] && [ "$stat" -eq 1 ] && [ -z "${ERROR##*File exists}" ]; then
|
||
|
|
||
|
duplicatef=$(_prepend_date "${fname}")
|
||
|
|
||
|
_link "${file}" "${archivedir}/${duplicated}/${duplicatef}"
|
||
|
printf "ERROR: File %s exists. Linked as %s. Check for duplicates.\n" "$fname" "$duplicatef"
|
||
|
elif [ "$stat" -gt 0 ]; then
|
||
|
printf "ERROR $stat: File %s exists. Nothing done. Use -f to force relinking new file.\n" "$fname"
|
||
|
printf %s "$ERROR"
|
||
|
fi
|
||
|
done
|
||
|
}
|
||
|
|
||
|
_prepend_date() {
|
||
|
fname="$1"
|
||
|
duplicated=$(dirname "$fname")
|
||
|
duplicatef=$(basename "${fname}")
|
||
|
printf "%s/%s-%s" "$duplicated" "$(date +%s)" "$duplicatef"
|
||
|
}
|
||
|
|
||
|
_link() {
|
||
|
#try to create a hard link to file
|
||
|
if [ "$DRYRUN" = "true" ]; then
|
||
|
echo ln "${1}" "${2}"
|
||
|
return
|
||
|
fi
|
||
|
{ ln "${1}" "${2}" >/dev/null; } 2>&1
|
||
|
}
|
||
|
|
||
|
_ensuredir() {
|
||
|
if [ "$DRYRUN" = "true" ]; then
|
||
|
echo mkdir -p "${1}"
|
||
|
return
|
||
|
fi
|
||
|
mkdir -p "${1}"
|
||
|
}
|
||
|
|
||
|
main "$@"
|