Fixa till bash-script för smart backup?
Jag har under många år använt mig av script och rsync för att skapa flera versioner av säkerhetskopiera kataloger med viktiga filer mellan datorer i mitt nätverk. För en tid sedan ersatte jag ett Synology NAS med ett antal Odroid HC2 med OMV. Och de säkerhetskopierar sig själva till varandra och till det gamla Synology NASet med hjälp av cron-jobb som kör script med rsync.
Jag har nyligen uppdaterat mitt script och kör det nu i skarp drift. Eftersom mitt script har utvecklats stegvis över många år så är det kanske inte så snyggt och polerat som det kan vara. Senast uppdaterade jag hur gamla snapshots automatiskt rensas bort över tid. Den delen som kör rsync är riktigt gammal och stabil. Scriptet tycks fungera utmärkt nu och jag känner inget större behov av att fortsätta putsa på det. Men tar tacksamt emot hjälp att putsa vidare.
Kanske andra här kan ha nytta av mitt script och kanske rent av kan förbättra det eller putsa lite på det och snygga till koden? Strukturera om? Göra koden mer enhetlig? Hitta och fixa buggar? Göra scriptet mer koncist och lättförståeligt? Skippa lagring av snapshot om inga ändringar gjorts? Förbättra loggningen? Rensa även bland dagliga snapshot? Ge det en maximalt fri licens? Annat?
Eller du kanske gör samma sak på ett helt annat mycket bättre sätt? Nyfiken!
Det är ingen större vits att öka prestanda, om scriptet körs på 0,5 sekunder eller 50 sekunder spelar ingen större roll. Scriptet körs med automatik vid 4-5-tiden på morgonen. Den tunga biten är att köra rsync då det skett ändringar.
Skriptet är med avsikt helt självständigt och har mycket få beroenden. Jag gör en ny kopia av scriptet för varje ny typ av snapshot, ändrar några variabler, och låter sedan cron köra alla script dagligen med ett hjälpscript all.sh. Funkar fint.
Vill någon testa bifogar jag ett script populate.sh för att skapa testfiler som kan simulera gamla snapshots.
Här är scripten, hugg in:
media_snapshot.sh
#!/usr/bin/env bash
# Versioned daily rsync snapshots with weekly and monthly purge.
# Uses rsync and hardlinks to create snapshots that looks like full backups
# and saves space by linking to files already copied in a previos snapshot.
# Don't use/change files in a snapshot. Copy the files/snapshot first.
# Don't store anything but snapshots in $destination
# Change these variables to customize:
destination='/sharedfolders/nas4/snapshots/media'
source='/media/nfs/nas2/media'
snapshot_name=media_$(date +%F_%T)
# Set number of daily, weekly and monthly backups to keep
keep_daily=7 # keep this number of daily backups
keep_weekly=8 # keep this number of weekly backups
keep_monthly=12 # keep this number of monthly backups
# Delete earlier failed attempt at making a snapshot, if any
rm -rf "$destination"/unfinished
# Find latest snapshot, if any
latest_snapshot=$(ls -rt "$destination" | tail -1)
# Create folder for this snapshot, unfinished so far...
mkdir -p "$destination"/unfinished
# Grab new snapshot. Use rsync to create hard links to files already in
# latest previous snapshot(s)
rsync -a --delete --stats \
--link-dest="$destination"/"$latest_snapshot" \
"$source" \
"$destination"/unfinished > "$destination"/unfinished/rsync.log
# Finished!
if [ $? -eq 0 ]; then
mv "$destination"/unfinished "$destination"/"$snapshot_name"
else
exit 1
fi
# Purge old backups
#
# Echo age of file $1 in days
#
age_in_days ()
{
(( age = ( $(date -d 'now' +%s) - $(date -r "$destination/$1" +%s) ) / 60 / 60 / 24 ))
echo $age
}
#
# Compare time diffs between an old backup and the two next newer
# $1 = Old backup
# $2 = Next next newer backup
# $3 = Target age interval between backups
# Echo 1 if the age interval between backups means that a snapshot
# between $1 and $2 can be purged
#
can_purge_next ()
{
(( diff2 = "$3" - ( $(age_in_days "$1" ) - $(age_in_days "$2" )) ))
# Can purge next?
if (( diff2 >= 0 )); then
echo 1
else
echo
fi
}
#
# Echo 1 if file $1 is older than $2 days
#
older ()
{
(( age = $(age_in_days "$1") ))
if (( age > "$2" )); then
echo 1
else
echo
fi
}
#
# Purge a set of backups
# $1 = Start age for backups to possibly purge, in days
# $2 = Stop age for backups to possibly purge, in days
# $3 = Target interval for backups to keep, in days
# $4-$# = Backups to check, sorted with oldest first
#
purge ()
{
days_start=$1
days_stop=$2
days_interval=$3
shift 3
# Fast forward to first snapshot to test
while [[ "$#" && $(older "$1" "$days_start") ]]; do
shift
done
# purge snapshots until stop
while [[ "$#" && $(older "$1" "$days_stop") ]]; do
curr=$1
next1=$2
next2=$3
purged=false
# really purge snapshots
while [[ $(older "$next1" "$days_stop") && $(can_purge_next "$curr" "$next2" "$days_interval") ]]; do
rm "$destination/$next1" -rf
shift
next1=$next2
next2=$3
done
shift
done
}
# calcultate boundaries between daily/weekly/monthly
((max_age_daily = keep_daily))
((max_age_weekly = max_age_daily + 7 * keep_weekly))
((max_age_monthly = max_age_weekly + 30 * keep_monthly))
# delete all backups older than max_age_monthly
find $destination -maxdepth 1 -type d -mtime +$max_age_monthly -print0 | xargs -0 rm -rf
# Purge monthly start stop interval snapshots oldest first
purge $max_age_monthly $max_age_weekly 30 $(ls -rt "$destination")
# Purge weekly
purge $max_age_weekly $max_age_daily 7 $(ls -rt "$destination")
exit 0
all.sh
#!/usr/bin/env bash
for f in *_snapshot.sh; do
bash "$f"
done
populate.sh
mkdir testdata
for d in {0..500}
do
touch -d "$d days ago" /home/adoby/Skrivbord/test/testdata/backup$d.txt
done
Linux och Android