Zum Inhalt

Backup am Mac mit Automator, Shell Skript und rsync

Eine Datensicherung von einem Mac zu openmediavault lässt sich mit Automator, Shell Skripten und rsync (fast) automatisieren. Ein Client ist dafür nicht notwendig.

Backup unverzichtbar aber doch irgendwie lästig, oder?

Das Sicherungsprozedere lässt sich mit ein mit

  • Automator
  • AppleScript
  • Shell Skripten

sehr gut (fast) automatisieren.

Hört sich kompliziert an, ist es aber nicht.

Das Schaubild zeigt grafisch, wie der gesamte Prozess aussieht.

MacOS, Automator, rsync, openmediavault

Als NAS im eigenen Netzwerk verwende ich für diese Anleitung ein openmediavault. Es läuft auf einem Raspberry Pi 4 mit 2 GB RAM. Am Pi hängen ein paar externe Festplatten, die bereits konfiguriert und für meinen Benutzer freigegeben worden sind. (Die Konfiguration von openmediavault ist nicht Teil dieser Anleitung.)

Das hier vorgestellte Setup funktioniert sehr zuverlässig. openmediavault ist meiner Meinung nach eine sehr gute und zuverlässige NAS-Lösung zu kommerziellen Anbietern Synology und QNAP und ist in Verbindung mit dem kleinen Raspberry Pi eine kostengünstige Alternative. Zudem sehr flexibel, da sich sehr viele unterschiedliche Festplatte und USB-Sticks an dem kleinen Einplatinenrechner anschließen lassen.

Auf dem Mac lassen sich mit Automator lokale Bash-Skripte aufrufen und damit beliebige Befehle und Operationen auf dem Mac ausführen.

Achtung

In diesem Beispiel wird die openmediavault-Freigabe nicht durch Automator, AppleScript oder das Shell Skript beim Mac eingebunden. Das muss manuell durch den Benutzer erfolgen.
Am Einfachsten funktioniert das Einbinden von openmediavault-Freigaben mit Finder.

Das AppleScript ist schnell erstellt. Die Ausgabe von rsync soll auch in der Application iTerm angezeigt werden. Wenn du ein anderes Terminal verwendest, dann musst du den Applikationsnamen ändern.

Das Shell Skript mit den rsync-Befehlen ist im Verzeichnis ~/Documents/rsync abgelegt.

Der Name des Bash Skript lautet backup-openmediavault-all.sh

Im nächsten Schritt wird in das Verzeichnis ~/Documents/rsync gewechselt, mit dem Befehl cd.

Zum Schluss wird das Shell Skript backup-openmediavault-all.sh aufgerufen.

tell application "iTerm"
    set newWindow to (create window with default profile)
    tell current session of newWindow
        write text "clear"
        write text "cd ~/Documents/rsync; ls"
        write text "./backup-openmediavault-all.sh"
        write text "exit"
    end tell
end tell

Shell Script für rsync backup-openmediavault-all.sh

In diesem Beispiel gibt es drei Shell Skripte die unterschiedlichen Funktionen erfüllen.

  • backup-openmediavault-all.sh = Hauptskript, dort werden Variablen, Verfügbarkeit der openmediavault-Freigaben abgefragt und die Unter-Skripte aufgerufen
  • backup-openmediavault-documents.sh = Unter-Skript - enthält die rsync-Befehle zum Sichern des Dokumenten-Verzeichnisses
  • backup-openmediavault-bilder.sh = Unter-Skript - enthält die rsync-Befehle zum Sichern des Bilder-Verzeichnisses

Wie das Ganze funktioniert und wie die Variablen vom Hauptskript an die beiden Unterskripte übergeben werden, habe ich in diesem Artikel beschrieben 👉 Shell Skript – Variablen an andere Skripte übergeben

Gib mir gerne einen Kaffee ☕ aus ❗️

Wenn dir meine Beiträge gefallen und geholfen haben, dann kannst du mir gerne einen Kaffee ☕️ ausgeben.

Donation via PayPalDonation via LiberaPay

Donation via Bitcoin
Bitcoin Address: bc1qfuz93hw2fhdvfuxf6mlxlk8zdadvnktppkzqzj

Das Hauptskript backup-openmediavault-all.sh

Das Shell Skript backup-openmediavault-all.sh kann so aussehen:

#!/usr/bin/env bash


###################################
# Variables
###################################

# Hostname
echo "Host = $(hostname)"

# Script name
SCRIPTNAME=$(basename -- "$0")
echo "SCRIPTNAME = ${SCRIPTNAME}"

# Location of your log file on your Mac
LOGFILE="${HOME}/rsync/openmediavault-backup-rsync-$(date +%Y-%m-%d_%H-%M-%S).log"
exec 1> ${LOGFILE}

if test -f "${LOGFILE}"
  then
    echo "++++++++++++++++++++++++++" >&1
    echo "$(date +%Y-%m-%d_%H-%M-%S) - Start ${SCRIPTNAME} on $(hostname)" >&1
    echo "++++++++++++++++++++++++++" >&1
    echo "" >&1
    echo "$(date +%Y-%m-%d_%H-%M-%S) - Log File already exists!" >&1
    echo "$(date +%Y-%m-%d_%H-%M-%S) - Log File location: ${LOGFILE}" >&1
  else
    touch ${LOGFILE}
    echo "++++++++++++++++++++++++++" >&1
    echo "$(date +%Y-%m-%d_%H-%M-%S) - Start ${SCRIPTNAME} on $(hostname)" >&1
    echo "++++++++++++++++++++++++++" >&1
    echo "" >&1
    echo "$(date +%Y-%m-%d_%H-%M-%S) - Log File newly created!" >&1
    echo "$(date +%Y-%m-%d_%H-%M-%S) - Log File location: ${LOGFILE}" >&1
fi

# PWD = Script location  = Mac
SCRIPTPWD="${HOME}/Documents/rsync/"

# Mount point for network share on local client = Mac
LOCAL_DESTINATION="${HOME}/Desktop"

# Name of Network Share Folder
declare -a REMOTE_MOUNTS=(Sicherung-benutzer)

# Backup storage = Mount Point
BACKUPSTORAGE="/Volumes/Sicherung-benutzer"

# Backup directory on MAC
# on local client for "rsync --delete" option
# folder on MAC where deleted files gets stored by rsync
BACKUPDIRECTORY="${HOME}/rsync/"

# Remote file server = openmediavault
# IP Address, URL, etc. 
REMOTE_SERVER='192.168.178.220'

# Remote user = openmediavault
REMOTE_USER='benutzername'


echo "" >&1
echo "----" >&1
echo "SCRIPTPWD = ${SCRIPTPWD}" >&1
echo "" >&1
echo "REMOTE_SERVER = ${REMOTE_SERVER}" >&1
echo "REMOTE_USER = ${REMOTE_USER}" >&1
echo "REMOTE_MOUNTS = ${REMOTE_MOUNTS}" >&1
echo "Remote folder off network share " >&1
echo "" >&1
echo "LOCAL_DESTINATION = ${LOCAL_DESTINATION}" >&1
echo "Folder for mount point on client for network share " >&1
echo "" >&1
echo "BACKUPSTORAGE = ${BACKUPSTORAGE}" >&1
echo "Shared folder of network share where files are copied " >&1
echo "" >&1
echo "BACKUPDIRECTORY = ${BACKUPDIRECTORY}" >&1
echo "Folder where rsync --delete saves folders and files on local client" >&1
echo "----" >&1
echo "" >&1



###################################
# Check storage availability
###################################
# If network share is not pingable, script will stop

echo "$(date +%Y-%m-%d_%H-%M-%S) - Check ${REMOTE_SERVER} Availability - Send PING" >&1
echo "Storage Address = ${REMOTE_SERVER}" >&1

ping -c1 ${REMOTE_SERVER} >&1
if [ $? -eq 0 ]
  then 
    echo "$(date +%Y-%m-%d_%H-%M-%S) - ${REMOTE_SERVER} available - CONTINUE" >&1
  else
    echo "" >&1
    echo "$(date +%Y-%m-%d_%H-%M-%S) - ${REMOTE_SERVER} NOT available - STOPP Script" >&1
    echo "$(date +%Y-%m-%d_%H-%M-%S) - BACKUP FAILED" >&1

    open -a "Console" >&1
    sleep 15
    osascript -e 'tell application "Console" to quit'

    exit 1
fi

echo "" >&1 



###################################
# Check mount point availability and call subscripts
###################################
# This sections calls the subscripts
# - backup-openmediavault-documents.sh
# - backup-openmediavault-bilder.sh


if [ -d ${BACKUPSTORAGE} ]

then
    echo "Already mounted ${BACKUPSTORAGE}" >&1
    echo "$(date +%Y-%m-%d_%H-%M-%S) ${BACKUPSTORAGE} Mount Point Available - START Backup Scripts" >&1
    echo "" >&1

    /bin/bash ${SCRIPTPWD}backup-openmediavault-documents.sh "${LOGFILE}" "${BACKUPDIRECTORY}" "${BACKUPSTORAGE}"

    echo "" >&1

    /bin/bash ${SCRIPTPWD}backup-openmediavault-bilder.sh "${LOGFILE}" "${BACKUPDIRECTORY}" "${BACKUPSTORAGE}"

    open -a "Console" >&1
    sleep 15
    osascript -e 'tell application "Console" to quit'

else
    echo "Not mounted" >&1
    sleep 5
    echo "$(date +%Y-%m-%d_%H-%M-%S) ${BACKUPSTORAGE} not mounted - NO BACKUP" >&1

    open -a "Console" >&1
    sleep 15
    osascript -e 'tell application "Console" to quit'

    exit 2
fi

###################################
# Delete Old Files
###################################

echo "$(date +%Y-%m-%d_%H-%M-%S) - Remove Files Older Than 10 Days in backup directory ${BACKUPDIRECTORY}" >&1

#find ${BACKUPDIRECTORY}* -mtime +10 -exec rm -rf {} \; >&1

# Delete files older than
find ${BACKUPDIRECTORY}* -mtime +7 -exec rm -r {} \; >&1

# Delete empty folders
find ${BACKUPDIRECTORY}* -empty -type d -delete >&1

exit 0

Unter-Skript backup-openmediavault-documents.sh

Das erste Unter-Skript sichert das Dokumenten-Verzeichnis eures Macs.

#!/usr/bin/env bash


SCRIPTNAME=$(basename -- "$0")

echo "+++++" >&1
echo "SCRIPTNAME = ${SCRIPTNAME}" >&1
echo "+++++" >&1

echo "Passed Variables to ${SCRIPTNAME}:" >&1
echo "----" >&1
echo "LOGFILE = $1" >&1
echo "BACKUPDIRECTORY = $2" >&1
echo "BACKUPSTORAGE = $3" >&1

echo "" >&1


###################################
# Backup Folder Definition
###################################
echo "" >&1
echo "----" >&1
echo $(date +%Y-%m-%d_%H-%M-%S) " - Start rsync process - 1st Backup Folder" >&1

#Source patch - local client = Mac
SOURCE="${HOME}/Documents/"
echo "SOURCE = ${SOURCE}" >&1

#Target path on BACKUPSTORAGE = openmediavault
TARGET="$3/dokumente/"
echo "TARGET = ${TARGET}" >&1

#Exclude from RSYNC file
EXCLUDEFILE="${HOME}/Documents/rsync/rsync-macos-excludefile.txt"
echo "EXCLUDEFILE = ${EXCLUDEFILE}" >&1

#BACKUP Directory
# In dieses Verzeichnis werden alle Dateien kopiert die während des rsync-Laufs durch die Option --delete gelöscht werden.
# Diese Option erzeugt eine hohe Systemlast und eine sehr lange Laufzeit des Skripts
#BACKUPDIRECTORY="$2"
echo "BACKUPDIRECTORY = $2" >&1


rsync -avhbP --iconv=UTF8,UTF8-MAC --stats --delete --log-file=$1 --backup-dir=$2 ${SOURCE} ${TARGET}

echo "----" >&1

echo "${SCRIPTNAME} - Beendet"

sleep 15

exit 0


###################################
#Starts rsync Process From Source to Target
###################################
# Copies the files from Source to Target

#     -a fasst folgende Optionen zusammen:
#     -r kopiert Unterverzeichnisse
#     -l kopiert symbolische Links
#     -p behält Rechte der Quelldatei bei
#     -t behält Zeiten der Quelldatei bei,
#     -g behält Gruppenrechte der Quelldatei bei
#     -o behält Besitzrechte der Quelldatei bei (nur root)
#     -D behält Gerätedateien der Quelldatei bei (nur root)
#     -v: zeigt während des Synchronisierens alle ausgeführten Schritte an
#     -h: human readable
#     -c: aktiviert einen Dateivergleich, basierend auf Prüfsumme und nicht auf Größe und Zeitstempel. Die eigentliche Prüfsummenbildung dauert DEUTLICH länger als der Vergleich Größe und Änderungs-Zeitstempel; andererseits werden überflüssige Kopiervorgänge (z.B. bei nur geänderter Änderungszeit) vermieden.
#     -n: simuliert nur was passieren würde ("dry run")
#     -b: sorgt dafür, dass durch die Option --delete gelöschte sowie alle veränderten Objekte gesichert werden (--backup-dir=)
#     -P: aktiviert folgende Optionen:
#     --progress Fortschrittsanzeige beim Transfer anzeigen
#     --partial Fortsetzung des Transfers bei Abbruch
#     --stats: zeigt einen ausführlicheren Bericht am Ende einer Übertragung an.
#     --delete: vergleicht Quellverzeichnisse und Zielverzeichnisse und sorgt dafür, dass Dateien, die im Quellverzeichnis nicht (mehr) vorhanden sind, im Zielverzeichnis gelöscht werden. Dies kann dazu führen, dass man ungewollt Dateien löscht, die man aber noch in der Sicherung behalten möchte.
#     --ignore-existing: Überspringt die vorhandenen Daten und schreibt nur die neuen

# Sync mit einem Mac
# Umlaute produzieren Fehler bei rsync

# von Linux zu MAC
# --iconv=UTF8-MAC,UTF8

# von Mac zu Linux
#--iconv=UTF8,UTF8-MAC

# Dieser Abschnitt erklärt die Syntax von rsync
#sudo rsync -av --stats --delete --exclude ${EXCLUDE} --exclude ${EXCLUDE1} --exclude ${EXCLUDE2} --exclude ${EXCLUDE3} --exclude ${EXCLUDE4} ${SOURCE} ${TARGET} --log-file=$1

# Ohne Log Files
#sudo rsync -av --stats --exclude-from=${EXCLUDEFILE} --delete ${SOURCE} ${TARGET}

Unter-Skript backup-openmediavault-bilder.sh

Das zweite Unter-Skript sichert die Bilder-Verzeichnisse.

#!/usr/bin/env bash


SCRIPTNAME=$(basename -- "$0")
echo "SCRIPTNAME = ${SCRIPTNAME}"

echo "+++++" >&1
echo "SCRIPTNAME = ${SCRIPTNAME}" >&1
echo "+++++" >&1

echo "Passed Variables to ${SCRIPTNAME}:" >&1
echo "----" >&1
echo "LOGFILE = $1" >&1
echo "BACKUPDIRECTORY = $2" >&1
echo "BACKUPSTORAGE = $3" >&1

echo "" >&1


###################################
# 1st Backup Folder Definition
###################################
echo "" >&1
echo "----" >&1
echo $(date +%Y-%m-%d_%H-%M-%S) " - Start rsync process - 1st Backup Folder" >&1

#Source patch - local client
SOURCE1="${HOME}/Pictures/2020/"
echo "SOURCE1 = ${SOURCE1}" >&1

#Target path on BACKUPSTORAGE
TARGET1="$3/pictures/2020/"
echo "TARGET1 = ${TARGET1}" >&1

#Exclude from RSYNC file
#EXCLUDEFILE1="${HOME}/Documents/rsync/rsync-excludefile-bilder.txt"
#echo "EXCLUDEFILE1 = ${EXCLUDEFILE1}" >&1

#BACKUP Directory
# In dieses Verzeichnis werden alle Dateien kopiert die während des rsync-Laufs durch die Option --delete gelöscht werden.
# Diese Option erzeugt eine hohe Systemlast und eine sehr lange Laufzeit des Skripts
#echo "BACKUPDIRECTORY1 = ${BACKUPDIRECTORY1}" >&1


rsync -avhbP --iconv=UTF8,UTF8-MAC --stats --delete --log-file=$1 --backup-dir=$2 ${SOURCE1} ${TARGET1}

echo "----" >&1


###################################
# 2nd Backup Folder Definition
###################################
echo "" >&1
echo "----" >&1
echo $(date +%Y-%m-%d_%H-%M-%S) " - Start rsync process - 2nd Backup Folder" >&1

#Source patch - local client
SOURCE2="${HOME}/Pictures/2021/"
echo "SOURCE2 = ${SOURCE2}" >&1

#Target path on BACKUPSTORAGE
TARGET2="$3/pictures/2021/"
echo "TARGET2 = ${TARGET2}" >&1

#Exclude from RSYNC file
#EXCLUDEFILE2=""
#echo "EXCLUDEFILE2 = ${EXCLUDEFILE2}" >&1

#BACKUP Directory
# In dieses Verzeichnis werden alle Dateien kopiert die während des rsync-Laufs durch die Option --delete gelöscht werden.
# Diese Option erzeugt eine hohe Systemlast und eine sehr lange Laufzeit des Skripts
#BACKUPDIRECTORY1="$2"
#echo "BACKUPDIRECTORY1 = ${BACKUPDIRECTORY1}" >&1


rsync -avhbP --iconv=UTF8,UTF8-MAC --stats --delete --log-file=$1 --backup-dir=$2 ${SOURCE2} ${TARGET2} 

echo "----" >&1


###################################
# 3rd Backup Folder Definition
###################################
echo "" >&1
echo "----" >&1
echo $(date +%Y-%m-%d_%H-%M-%S) " - Start rsync process - 3rd Backup Folder" >&1

#Source patch - local client = Mac
SOURCE3="${HOME}/Pictures/2022/"
echo "SOURCE3 = ${SOURCE3}" >&1

#Target path on BACKUPSTORAGE = openmediavault
TARGET3="$3/pictures/2022/"
echo "TARGET3 = ${TARGET3}" >&1

#Exclude from RSYNC file
#EXCLUDEFILE3=""
#echo "EXCLUDEFILE3 = ${EXCLUDEFILE3}" >&1

#BACKUP Directory
# In dieses Verzeichnis werden alle Dateien kopiert die während des rsync-Laufs durch die Option --delete gelöscht werden.
# Diese Option erzeugt eine hohe Systemlast und eine sehr lange Laufzeit des Skripts
#echo "BACKUPDIRECTORY3 = ${BACKUPDIRECTORY3}" >&1


rsync -avhbP --iconv=UTF8,UTF8-MAC --stats --delete --log-file=$1 --backup-dir=$2 ${SOURCE3} ${TARGET3}

echo "----" >&1

echo "${SCRIPTNAME} - Beendet"

sleep 15

exit 0



###################################
#Starts rsync Process From Source to Target
###################################
# Copies the files from Source to Target

#     -a fasst folgende Optionen zusammen:
#     -r kopiert Unterverzeichnisse
#     -l kopiert symbolische Links
#     -p behält Rechte der Quelldatei bei
#     -t behält Zeiten der Quelldatei bei,
#     -g behält Gruppenrechte der Quelldatei bei
#     -o behält Besitzrechte der Quelldatei bei (nur root)
#     -D behält Gerätedateien der Quelldatei bei (nur root)
#     -v: zeigt während des Synchronisierens alle ausgeführten Schritte an
#     -h: human readable
#     -c: aktiviert einen Dateivergleich, basierend auf Prüfsumme und nicht auf Größe und Zeitstempel. Die eigentliche Prüfsummenbildung dauert DEUTLICH länger als der Vergleich Größe und Änderungs-Zeitstempel; andererseits werden überflüssige Kopiervorgänge (z.B. bei nur geänderter Änderungszeit) vermieden.
#     -n: simuliert nur was passieren würde ("dry run")
#     -b: sorgt dafür, dass durch die Option --delete gelöschte sowie alle veränderten Objekte gesichert werden (--backup-dir=)
#     -P: aktiviert folgende Optionen:
#     --progress Fortschrittsanzeige beim Transfer anzeigen
#     --partial Fortsetzung des Transfers bei Abbruch
#     --stats: zeigt einen ausführlicheren Bericht am Ende einer Übertragung an.
#     --delete: vergleicht Quellverzeichnisse und Zielverzeichnisse und sorgt dafür, dass Dateien, die im Quellverzeichnis nicht (mehr) vorhanden sind, im Zielverzeichnis gelöscht werden. Dies kann dazu führen, dass man ungewollt Dateien löscht, die man aber noch in der Sicherung behalten möchte.
#     --ignore-existing: Überspringt die vorhandenen Daten und schreibt nur die neuen

# Sync mit einem Mac
# Umlaute produzieren Fehler bei rsync

# von Linux zu MAC
# --iconv=UTF8-MAC,UTF8

# von Mac zu Linux
#--iconv=UTF8,UTF8-MAC

# Dieser Abschnitt erklärt die Syntax von rsync
# rsync -av --stats --delete --exclude ${EXCLUDE} --exclude ${EXCLUDE1} --exclude ${EXCLUDE2} --exclude ${EXCLUDE3} --exclude ${EXCLUDE4} ${SOURCE} ${TARGET} --log-file=${LOGFILE}

# Ohne Log Files
# rsync -av --stats --exclude-from=${EXCLUDEFILE} --delete ${SOURCE} ${TARGET}

Das Automator-Skript lässt sich mit einigen Klicks auch auf dem Schreibtisch ablegen und so sehr einfach mit einem Doppelklick starten.

Die Anleitung dazu findest du in diesem Artikel 👉 Bash Skripte mit Automator starten

Gib mir gerne einen Kaffee ☕ aus ❗️

Wenn dir meine Beiträge gefallen und geholfen haben, dann kannst du mir gerne einen Kaffee ☕️ ausgeben.

Donation via PayPalDonation via LiberaPay

Donation via Bitcoin
Bitcoin Address: bc1qfuz93hw2fhdvfuxf6mlxlk8zdadvnktppkzqzj

Source

Photo by Jainath Ponnala on Unsplash