pedant
Goto Top

Bash-Skript mit rsync (Anfänger bittet um Hilfe)

Hallo,

unter Windows fühle ich mich zuhause, doch was Linux angeht bin ich recht unerfahren.
Jetzt möchte ich eine Aufgabe mit einem bash-Skript lösen.
Da ich, wie gesagt, von Linux kaum eine Ahnung habe, habe ich zwar ein Skript schreiben können, kann aber nicht beurteilen, ob das zweckmäßig ist.
Mir erscheint das Skript zielführend zu sein, aber ich habe keine Ahnung wo die Falltüren sind und welche himmelschreienden Anfängerfehler ich da reingetippt habe.
Meine Bitte wäre jetzt, dass sich jemand erbarmt, sich das Skript anzusehen und/oder auf meine Fragen einzugehen.
Im Moment habe ich weder Zeit noch Neigung, mich ins Neuland "Linux" soweit einzuarbeiten, dass ich ohne Hilfe auskäme, daher hoffe ich Euer KnowHow nutzen zu dürfen, ohne dass mir das als zu bequemer Weg vorgeworfen wird.


1. Die Aufgabe
Ich möchte Dateien eines Windows-Fileserver auf einem NAS sichern.
Auf dem NAS läuft ein Linux (DSM 6.1.4-15217 Update 5 auf Synology RS3617xs+).
Der aktive Part soll dabei das NAS sein und auf den Windowsserver möchte ich keine zusätzlichen Tools installieren.
SSH und sonstige Sicherheitsmaßnahmen benötige ich nicht.
Die Boardmittel und Zusatzpakete des NAS, die Backups ohne Skript ermöglichen, habe ich weitgehend ausprobiert und war damit nicht zufrieden.
Google zeigte mir ein paar fertige rsync-Skripte, die mir aber alle unnötig komplex erschienen und deren Funktionen für mich nicht verständlich waren.
Ich würde ein möglichst schlankes Skript bevorzugen, das ich gänzlich nachvollziehen kann.


2. Der Lösungsansatz
Ich mounte am NAS die Netzwerkfreigabe des Windows-Fileservers, auf der die zu sichernden Dateien liegen.
Dann kopiere ich mit dem NAS, die Dateien in einen lokalen Ordner des NAS.
Danach unmounte ich die Quelle wieder.
Zum Kopieren nutze ich rsync.
Die Ausgaben von rsync schreibe ich in eine Logdatei (2017-01-05_rsynclog.txt).
Im Skript erzeuge ich weitere Ausgaben, die in eine weitere Logdatei (2017-01-05_scriptlog.txt) geschrieben werden.
Die Trennung der Logs soll dazu dienen, dass das Kontroll-Log schlank bleibt und leicht ausgewertet werden kann.
Im Falle eines Fehlers oder Neugier, kann ich im rsync-Log nachsehen was tatsächlich passiert ist.


3. Der Status
Mein Skript habe ich noch nicht getestet, ich habe es heute geschrieben, um hier nicht mit leeren Händen dazustehen.
Die Kern-Befehle sudo mount.cifs... und sudo rsync... habe ich aber gestern in einer Telnet-Sitzung ausprobiert und sie funktionieren.


4. Das Script
#!/bin/bash

# Konfiguration
## Windows
benutzername=Administrator
kennwort=geheim
windowsquelle=//WinServer/Freigabe/Ordner/
## Linux
sudokennwort=auchgeheim
lokalequelle=/volume1/admin/mount
lokalesziel=/volume1/Backup/Winserver
testdatei==${lokalequelle}/lokal.txt # Die Testdatei muss in diesem Ordner existieren

# Logdatei erzeugen
datum=$(date +%Y-%m-%d)
scriptlogdatei=/volume1/admin/log/${datum}_scriptlog.txt
rsynclogdatei=/volume1/admin/log/${datum}_rsynclog.txt
echo "$(date +%H:%M:%S) - INFO: Starte Backup von $windowsquelle"  
echo "$(date +%H:%M:%S) - INFO: Starte Backup von $windowsquelle" > $scriptlogdatei  

# Quelle mounten
echo "$(date +%H:%M:%S) - INFO: Windowsquelle verbinden..."  
echo "$(date +%H:%M:%S) - INFO: Windowsquelle verbinden..." >> $scriptlogdatei  
if [ ! -f $testdatei ]
  then
    echo "$(date +%H:%M:%S) - ERROR: Die Testdatei existiert nicht im lokalen Ordner."  
    echo "$(date +%H:%M:%S) - ERROR: Die Testdatei existiert nicht im lokalen Ordner." >> $scriptlogdatei  
    exit
fi
echo $sudokennwort | sudo -S mount.cifs -o ro,username=${benutzername},password=${kennwort} $windowsquelle $lokalequelle
if [ -f $testdatei ]
  then
    echo "$(date +%H:%M:%S) - ERROR: Das Mounten hat nicht funktioniert."  
    echo "$(date +%H:%M:%S) - ERROR: Das Mounten hat nicht funktioniert." >> $scriptlogdatei  
    exit
fi

# Dateien kopieren
echo "$(date +%H:%M:%S) - INFO: Dateien kopieren: $windowsquelle -> $lokalesziel ..."  
echo "$(date +%H:%M:%S) - INFO: Dateien kopieren: $windowsquelle -> $lokalesziel ..." >> $scriptlogdatei  
echo $sudokennwort | sudo -S rsync --delete -a --no-perms --no-owner --no-group -W --verbose ${lokalequelle}/ $lokalesziel > $rsynclogdatei
fehlercode=$?
if [ $fehlercode -eq 0 -o $fehlercode -eq 24 ]
  then
    echo "$(date +%H:%M:%S) - INFO: Das Kopieren wurde erfolgreich beendet."  
    echo "$(date +%H:%M:%S) - INFO: Das Kopieren wurde erfolgreich beendet." >> $scriptlogdatei  
  else
    echo "$(date +%H:%M:%S) - ERROR: Beim Kopieren trat der Fehler $fehlercode auf."  
    echo "$(date +%H:%M:%S) - ERROR: Beim Kopieren trat der Fehler $fehlercode auf." >> $scriptlogdatei  
fi

# Quelle unmounten
echo "$(date +%H:%M:%S) - INFO: Windowsquelle trennen..."  
echo "$(date +%H:%M:%S) - INFO: Windowsquelle trennen..." >> $scriptlogdatei  
echo $sudokennwort | sudo -S umount $lokalequelle
fehlercode=$?
if [ ! $fehlercode -eq 0 ]
  then
    echo "$(date +%H:%M:%S) - ERROR: Das Unmounten hat nicht funktioniert. Fehler: $fehlercode"  
    echo "$(date +%H:%M:%S) - ERROR: Das Unmounten hat nicht funktioniert. Fehler: $fehlercode" >> $scriptlogdatei  
    exit
fi

echo "$(date +%H:%M:%S) - INFO: Backup erfolgreich abgeschlossen."  
echo "$(date +%H:%M:%S) - INFO: Backup erfolgreich abgeschlossen." >> $scriptlogdatei  


5. Die Automatisierung
Wie ich das Skript zeitgesteuert starten kann, weiß ich.
Wie ich das erzeugte Log auswerten kann, weiß ich auch.
Also Alles außerhalb des Skriptes bekomme ich alleine hin.


6. Meine Fragen

6.1 Linux mount
Unter Windows kann ich direkt auf eine Freigabe zugreifen.
Beispiel: xcopy \\WinServer\Freigabe\Ordner\*.* D:\Backup\
Wird dabei - warum auch immer - die Verbindung unterbrochen, so wird auch der Kopiervorgang abbrechen.
Bei Linux scheint das so nicht zu gehen (oder doch?)
Jetzt mache ich mir folgende Sorge:
Wenn ich einen tatsächlich vorhandenen, lokalen Ordner nutzen muss, um darin die Freigabe zu mounten, ist dieser Ordner auch dann noch vorhanden, wenn die Verbindung zum Windowsserver abbricht.
Der lokale Ordner, der zum Mounten verwendet wird, enthält aber nicht die Dateien, die ich kopieren möchte.
Wird rsync in Kombination mit --delete dann feststellen, dass die vortags gesicherten Dateien im Ziel, nicht in der Quelle sind und daher - wie angewiesen - mein Backup (das Ziel) leeren, obwohl genau jetzt der Fall eingetreten sein könnte, in dem ich das Backup benötigen würde?

6.2 Mountstatus prüfen
Im lokalen Ordner, den ich zum Mounten nutze, habe ich eine lokale Datei lokal.txt abgelegt.
Wenn der Ordner nicht zum Mounten genutzt ist, dann habe ich lokal Zugriff auf diese Datei.
Ist der Ordner aber zum Mounten genutzt, so wird der tatsächliche, lokale Inhalt verborgen und nur der entfernte (gemountete) Inhalt zur Verfügung gestellt.
Finde ich die Datei lokal.txt im Mount-Ordner, dann ist dort nicht gemountet.
Ob das eine elegante Fallunterscheidung ist, ich weiß es nicht.

6.3 sudo im Script
Wie kann ich einen Befehl ausführen lassen, der sudo benötigt, ohne manuell das dafür notwendige Kennwort von Hand eingeben zu müssen?

6.4 Die rsync-Optionen
Der Parameter -a kombiniert diverse Parameter: -a = --archive = -rlptgoD
Ich frage mich, ob die enthaltenen Parameter -g = -group und -o = -owner für mich sinnvoll sind.
Alle Dateien und Ordner in der zu sichernen Windows-Quelle, sind einfach nur Daten, für die keine speziellen Berechtigungen gesetzt sind.
Jeder, der Zugriff auf die Freigabe des Windows-Fileserver hat, kann dort machen was er will.
Im Falle eines Restores, möchte ich unter Windows die Dateien vom Backupordner des NAS über's Netzwerk wieder auf den Windows-Fileserver kopieren.
Dabei und anschließend möchte ich nicht auf Schwierigkeiten treffen ala "Sie benötigen die Berechtigung von XY..." um dies oder das zu tun.
Sind die Parameter -g = -group und -o = -owner in diesem Sinne hilfreich oder kortraproduktiv?

Vielen Dank in Voraus
Gruß Frank

EDIT: Punkt 4. "Das Script" mit Änderungen aktualisiert

Content-ID: 360023

Url: https://administrator.de/contentid/360023

Printed on: November 5, 2024 at 06:11 o'clock

135111
Solution 135111 Jan 05, 2018 updated at 13:34:38 (UTC)
Goto Top
Hi,
ich fang einfach mal an ein paar Fragen des Romans zu beantworten face-smile.
$datum=$(date +%y-%m-%d)
Datum wolltest du 4-stellig dann musst du das y groß schreiben %Y.
testdatei==${lokalequelle}/lokal.txt
Fehler, doppeltes Gleichheitszeichen.
Zeile 31
if [ -f $testdatei ]
Vergessen zu negieren? Du wolltest doch abbrechen wenn Datei nicht existiert.
6.3 sudo im Script
Kennwort des Linux Users bitte nicht hinterlegen, dazu nutzt man die sudoers Datei in der man eintragen kann welcher User welche Befehle oder Skripte ausführen mit sudo (auch ohne Passwort)ausführen kann.
https://stackoverflow.com/questions/25215604/use-sudo-without-password-i ...
https://wiki.ubuntuusers.de/sudo/Konfiguration/
z.B. mit
userxyz ALL=(ALL:ALL) NOPASSWD:ALL
Alles ohne Passwort erlauben, kannst du ja auf die Befehle einschränken die du brauchst.

Alternativ startest du das ganze Skript elevated dann ist ein sudo im Skript nicht nötig.
6.2 Mountstatus prüfen
Prüfe den exit code des mount Befehls, das sollte hier reichen.
Beispiel: xcopy \\WinServer\Freigabe\Ordner\*.* D:\Backup\
Wird dabei - warum auch immer - die Verbindung unterbrochen, so wird auch der Kopiervorgang abbrechen.
Bei Linux scheint das so nicht zu gehen (oder doch?)
Doch rsync wird dir entsprechende Fehlermeldungen werfen.
Sind die Parameter -g = -group und -o = -owner in diesem Sinne hilfreich oder kortraproduktiv?
Brauchst du hier nicht, lässt sich auch trotz bei Verwendung von -a ausnehmen ohne jetzt -rlptD zu schreiben
rsync -av --no-perms --no-owner --no-group ............

Gruß @135111
aqui
aqui Jan 05, 2018, updated at Jan 06, 2018 at 11:15:37 (UTC)
Goto Top
Wäre doch eher etwas für die Rubrik Bash & Shell Batch & Shell hier. Dann lesen es auch die richtigen Leute face-wink
(P.S.: Kann man nachträglich noch verschieben...)
Pedant
Pedant Jan 05, 2018 at 18:46:08 (UTC)
Goto Top
Hallo fuerte,

Zitat von @135111:
ich fang einfach mal an ein paar Fragen des Romans zu beantworten face-smile.
Sorry für den vielen Text und Danke für Deine Hinweise.

Das Meiste davon habe ich jetzt in das Skript eingebaut bwz. korrigiert und im Ausgangspost aktualisiert.
Mittlerweile habe ich das Skript auch insgesamt getestet und dabei ein paar weitere Korrekturen und Kosmetik vorgenommen.
Es läuft jetzt ohne Fehler durch, es sei denn ich provoziere Fehler, die dann scheinbar auch an richtiger Stelle abgefangen werden.

Zitat von @135111:
Kennwort des Linux Users bitte nicht hinterlegen, dazu nutzt man die sudoers Datei
Dem Hinweis konnte ich nicht nachkommen, da auf meinem System kein visudo existiert, zumindest erhalte ich bei
sudo visudo den Hinweis: sudo: visudo: command not found
Da das Kennwort des Windows-Users auch im Klartext hinterlegt ist, ist es nur fair, wenn das auch für den Linux-User gilt.
(Okay, kein gutes Argument, aber jeder im Lan kennt das Kennwort ohnehin.)

Deiner verlinkten Anweisung zufolge sollte ich aber eventuelle Änderungen unbedingt mit visudo machen.
Die Datei /etc/sudoers habe ich allerdings gefunden.
Unverändert sieht sie so aus:
## sudoers file.

# Enable logging of a command's output. 
# Use sudoreplay to play back logged sessions.
Defaults syslog=authpriv

# Allow root to execute any command
root ALL=(ALL) ALL

# Allow members of group administrators to execute any command
%administrators ALL=(ALL) ALL

# Configure privilege of wheel group
Cmnd_Alias SHELL = /bin/ash, /bin/sh, /bin/bash
Cmnd_Alias SU = /usr/bin/su
%wheel ALL=(ALL) NOPASSWD: ALL, !SHELL, !SU


Zitat von @135111:
Doch rsync wird dir entsprechende Fehlermeldungen werfen.
Was das angeht bin ich noch nicht ganz beruhigt.
Das Ausfallen der Verbindung als solches kann rsync ja nicht bemerken, da es lediglich zwei lokale Ordner syncen soll.
Dass der Quellordner virtuell mit den Inhalten eines Fileservers gefüllt ist (oder war), kann es doch nicht erkennen, außer dass der Ordner plötzlich leer ist.
Das Mounten hat doch eher was von einer Anrufweiterschaltung, von der ich als Anrufender auch nichts mitbekomme.
Wie würde rsync sich verhalten, wenn beim Erstellen seiner "incremental file list" die Verbindung abbricht und rsync ab dann mit einem leeren, lokalen Ordner weiter macht?
Könnte es dann sein, dass rsync annimmt, dass im Ziel viele Dateien liegen, die nicht in der Quelle sind und diese dann brav im Ziel löscht?

Nochmal vielen Dank für Deine Mühe und Deine Tipps.

Zitat von @aqui:
Wäre doch eher etwas für die Rubrik Bash & Shell hier.
@aqui
Die Rubrik heißt Batch & Shell und das hatte ich (voreingenommen) als "Windows" abgestempelt.
Du kannst meine Frage aber gerne verschieben, wohin sie auch immer am besten passen mag.

Gruß Frank
135111
Solution 135111 Jan 06, 2018 updated at 09:03:35 (UTC)
Goto Top
Dem Hinweis konnte ich nicht nachkommen, da auf meinem System kein visudo existiert
Das brauchst du auch nicht unbedingt, das manuelle Eintragen der obigen Zeile in die sudoers tuts natürlich auch.
Wie würde rsync sich verhalten, wenn beim Erstellen seiner "incremental file list" die Verbindung abbricht
Es wirft einen Fehler weil ein SMB Timeout getriggert wird, der Ordner ist also dann nicht automatisch leer nur weil die Verbindung abbricht, der Zugriff auf das Verzeichnis hängt quasi in der Luft.

Zusätzlich kannst du natürlich einen Bit-Vergleich hinterherschieben, ich persönlich würde hier aber statt nur mit rsync zu arbeiten das ganze gleich versioniert mit rsnapshot aufbauen. Somit hast du immer eine gewisse Anzahl an Snapshots in der Hinterhand. Rsnapshot basiert auf rsync und läuft auch dort wo rsync läuft.
http://rsnapshot.org
Pedant
Pedant Jan 06, 2018 at 10:09:42 (UTC)
Goto Top
Hallo fuerte,

Zitat von @135111:
...selbst hier getestet.
super, danke.

Zitat von @135111:
das manuelle Eintragen der obigen Zeile in die sudoers tust natürlich auch.
Wie schon erwähnt läuft das ganze auf einem NAS.
Starte ich das Skript in einer Telnetsitzung, muss ich das Kennwort eingeben.
Nutze ich aber die Aufgabenplanung der NAS-Software (Webinterface), kann ich auswählen das Skript als root ausführen zu lassen und dann läuft es auch ohne Kennwort (mit der unveränderten sudoers).
Den "Eingriff" kann ich mir daher sparen und das echo $sudokennwort | sudo -S command hab ich auch wieder rausgeworfen.

Zitat von @135111:
mit rsnapshot ... hast du immer eine gewisse Anzahl an Snapshots in der Hinterhand

rsnapshot hatte ich auch schon im Sinn und mich gerade an den Rechner gesetzt, um mich damit zu beschäftigen.
http://www.synology-wiki.de/index.php/Rsnapshot
Dennoch danke für die Anregung.

Jetzt habe ich erstmal Einiges zu lesen und auszuprobieren.
Ich melde mich wieder, sollte ich auf Schwierigkeiten stoßen oder eine fertige Lösung haben.
(Bis dahin halte ich den Thread noch offen, obwohl die Ausgangsfragen beantwortet wurden.)


Nur am Rande erwähnt. (Weiterlesen ist optional)...

Das NAS hatte ich jetzt neu angeschafft um endlich mehrere Missstände zu beheben und ein paar Verbesserungen einzuführen:

1. Katastrophenschutz
Meine Fileserver sichern sich bisher gegenseitig und alle sind mit Raid5+1HS ausgestattet.
Um Datenverlust wegen Hardwareausfall mache ich mir daher wenig Sorgen, aber alle Server stehen in einem Rack im selben Serverraum im Keller. Brand, Hochwasser o.ä. und alles wäre weg.
Das NAS kommt in ein anderes Gebäude, das auch am Lan angeschlossen ist.
2. Mehr Speicherplatz auf den Servern
Auf den Fielservern werde ich mehr Platz haben, weil ich dort keinen Platz mehr für die gegenseitigen Backups vorhalten muss.
3. Schutz vor den Auswirkungen von Virenbefall
Mein Netzwerk ist von innen sehr offen und das soll auch so bleiben, aber für einen Verschlüsselungstrojaner wäre es ein gefundenes Fressen.
Das NAS für die Backups möchte ich von dieser Offenheit ausgrenzen.
Daher soll keiner aus dem Lan schreibenden Zugriff auf das NAS erhalten, was mich dazu brachte die Backups vom NAS ausführen zu lassen, daher meine Beschäftigung mit rsync.
Wenn allerdings alle Quellen korrumpiert würden, würde beim nächsten täglichen Backup auch dieses korrumpiert sein.
Wenn ich ein Generationen-Backup mache, das täglich die geänderten Dateien sichert und die alten Versionen erst nach einer Woche verwirft, sollte ich Zeit genug haben zu merken, dass böse Dinge im Gange sind.
Im Moment habe ich etwa 16 TB an Dateien in den Quellen. Die täglichen Änderungen sind vergleichsweise eher klein.
Im Falle einer (unerwünschten) Verschlüsselung der Quellen, wären alle Quelldateien geändert und das inkrementelle Backup müsste dann statt ein paar GB die ganzen 16 TB sichern. Das hätte eine auffällige Backup-Berichts-E-Mail zur Folge, selbst wenn ansonsten niemand im Büro wäre, der sich über korrumpierte Dateien auf den Servern wundern würde.

Gruß Frank
aqui
aqui Jan 06, 2018 updated at 11:17:22 (UTC)
Goto Top
Du kannst meine Frage aber gerne verschieben,
Sorry für den Dreckfuhler, denn mit Bashing hat das natürlich nichts zu tun.
Deinen Beitrag können andere Forenteilnehmer natürlich NICHT so mirnixdirnix verschieben, wäre ja auch fataler Unsinn wenn das gehen würde.
Hier bist also nur DU selber als TO (=Thread Owner) gefragt das in die richtige Rubrik zu schieben. Batch und Shell gibt es übrigens bei Unix schon Jahrzehnte länger als bei Winblows. Soviel zur Voreingenommenheit face-wink
Pedant
Pedant Jan 06, 2018 at 13:35:20 (UTC)
Goto Top
Hallo aqui,

Zitat von @aqui:
Du kannst meine Frage aber gerne verschieben,
Hier bist also nur DU selber als TO (=Thread Owner) gefragt das in die richtige Rubrik zu schieben.
es war mir gar nicht bewusst, dass ich meine Beiträge selbst verschieben kann.
Ich hab's jetzt einfach gemacht, indem ich mein Ausgangspost bearbeitet und dort eine andere Rubrik auswählt habe.
Es scheint funktioniert zu haben.
Dich hatte ich zum Verschieben aufgefordert, weil ich dachte ich dürfe das nicht und Du bist hier Administrator.

Zitat von @aqui:
Batch und Shell gibt es übrigens bei Unix schon Jahrzehnte länger als bei Winblows. Soviel zur Voreingenommenheit face-wink
Ja, ich weiß - 01.01.1970 - deswegen schrieb ich ja "voreingenommen".

Gruß Frank
Pedant
Pedant Jan 06, 2018 at 19:13:56 (UTC)
Goto Top
Hallo,

Zitat von @Pedant:
Zitat von @135111:
das manuelle Eintragen der obigen Zeile in die sudoers tuts natürlich auch.
Ich habe es jetzt per vi eingetragen.

Zitat von @Pedant:
Jetzt habe ich erstmal Einiges zu lesen und auszuprobieren.
Es hat einige Zeit gedauert bis ich rsnapshot endlich auf dem Nas installiert hatte.
Dann folgte Konfigurieren und Testen.

Ich kam dann an den Punkt festzustellen, dass es sehr unflexibel ist.
Man muss alles in einer conf definieren und anschließend kann man immer nur alle Quellen sichern und die landen dann immer im selben Ziel.
Das gefällt mir nicht gut und da ich für jede Quelle einen andere Freigabe mounten muss, müsste ich vor dem Start alle Quellen mounten, statt einer nach der anderen (immer die, die ich gerade brauche).
Da rsnapshot sich auch nur des rsync' bedient, habe ich mir das genauer angesehen.
Dessen Parameter --link-dest=/pfad/vorhergehender_Backupordner machte die eigentliche Arbeit.
Ich habe mein Skript jetzt so umgebaut, dass es diesen Parameter nutzt und die Ordnerrotation zuvor abhandelt.
Noch ist es nicht soweit, dass ich's posten möchte, nur mal die rsync-relevanten Zeilen:
...
# Dateien kopieren
if [ "$fullbackup" == "true" ]  
  then
    echo "$(date +%H:%M:%S) - INFO: Vollsicherung: $windowsquelle -> ${lokalesziel}/0 ..."  
    echo "$(date +%H:%M:%S) - INFO: Vollsicherung: $windowsquelle -> ${lokalesziel}/0 ..." >> $scriptlogdatei  
    sudo /opt/bin/rsync -a --no-perms --no-owner --no-group -W --verbose --stats ${lokalequelle}/ ${lokalesziel}/0 > $rsynclogdatei
  else
    echo "$(date +%H:%M:%S) - INFO: Inkrementelle Sicherung: $windowsquelle -> ${lokalesziel}/0 ..."  
    echo "$(date +%H:%M:%S) - INFO: Inkrementelle Sicherung: $windowsquelle -> ${lokalesziel}/0 ..." >> $scriptlogdatei  
    sudo /opt/bin/rsync -a --no-perms --no-owner --no-group -W --verbose --stats --link-dest=${lokalesziel}/1 ${lokalequelle}/ ${lokalesziel}/0 > $rsynclogdatei
fi  
touch ${lokalesziel}/0/
...

Das Installieren von rsnapshot war aber nicht vergebens.
Es brachte rsync in der Version 3.1.2 mit sich. Zuvor war 3.0.9 installiert.
Der entscheidene Unterschied ist, dass zwar Beide den Parameter --link-dest=DIR kennen, aber das Ältere kopiert trotzdem Dateien neu, statt nur Hardlinks anzulegen. Ein Bug, der es einem Neuling nicht einfacher macht.

Gruß Frank
Pedant
Pedant Jan 07, 2018, updated at Jan 21, 2018 at 11:57:29 (UTC)
Goto Top
Hallo,

mit meinem Skript bin ich endlich fertig.
Jetzt wollte ich nur noch meine Lösung mitteilen.

Zuvor noch vielen Dank an fuerte für seine Hilfe hier und einen schönen Gruß an aqui.


Ich habe das Skript in fünf Teile aufgeteilt.

1. Hauptskript
backups_ausfuehren.su
Das ist das Skript, das ich zeitgeplant, automatisch ausführen lasse.
Es sucht alle Aufgabenskripte (siehe 5.)
Dann liest es diese Skripte nacheinander ein und lässt jeweils das Workerskript damit laufen.
Am Anfang der Datei sind vier Zeilen zur individuellen Grundkonfiguration vorhanden.
Alles andere braucht nicht verändert werden.


2. Konfiguration
konfiguration.sh
Ergänzend zur Konfiguration im Hauptskript, sind hier alle weiteren, allgemeine Konfigurationen vorzunehmen, also die Anpassung an die individuelle Systemsituation.

3. Funktionen
funktionen.sh
In diesem Skript sind lediglich "funtions" ausgelagert, die vom Hauptskript und/oder Workerskript genutzt werden.
Hier muss nichts angepasst werden.

4. Workerskript
psync.su
Dieses Skript erledigt die eigentliche Arbeit.
Es wird je einmal pro Backupaufgabe vom Hauptskript aufgerufen.
(Es direkt aufzurufen ist nicht sinnvoll, es ist funktionell vom Hauptskript abhängig.)
Für jede Aufgabe legt es ein versioniertes Backup an.
Die Versionstiefe kann für jeden Backupjob getrennt im zugehörigen Aufgabenskript definiert werden.
Bei bspw. 7 Versionen werden die Backup-Unterordner 0-6 gepflegt.
Bei einer Ausführung pro Tag wäre das also ein Backup, das die letzten 7 Tage sichert.
Im Prinzip macht das Skript dasselbe wie rsnapshot.
Für jede Aufgabe erstellt es ein eigenes Paar Logdateien, zusätzlich zu Einträgen im Sammel-Log, welches für jede Ausführung des Hautskripts angelegt wird.
Scheitert es bei einer Aufgabe, macht das Rahmenskript dennoch mit der nächsten Aufgabe weiter.
Hier muss nichts angepasst werden.

5. Aufgabenskripte
sichern_eine-aufgabe.su
sichern_andere-aufgabe.su
usw.
Das sind Einzel-Skripte, die jeweils eine Backupaufgabe definieren.
Im Grunde sind es lediglich Config-Dateien.
Um eine neue Aufgabe zu erstellen, muss lediglich ein Aufgabenskript kopiert, benannt und der Aufgabe entsprechend, individuell angepasst werden.
Sie entsprechen namentlich alle einem, in der Konfiguration zu definierenden Muster, bspw. sichern_*.sh, damit sie vom Hauptskript gefunden und benutzt werden.
Zum Löschen einer Aufgabe kann die zugehörige Datei gelöscht, verschoben oder umbenannt werden. Beim Umbenennen ist darauf zu achten, dass der Dateiname nicht mehr dem definierenden Muster entspricht.


Jetzt noch die Codes..


1. Hauptskript backups_ausfuehren.su
#!/bin/bash

### Hauptskript ###
  # zur Ausführung von Backups aufzurufen

# Konfiguration
hauptskriptverzeichnis="/volume1/admin/scripte/backup"  
configdatei="${hauptskriptverzeichnis}/konfiguration.sh"  
functionsdatei="${hauptskriptverzeichnis}/funktionen.sh"  
workerskriptdatei="${hauptskriptverzeichnis}/psync.sh"  
#weitere Konfiguration: configdatei


# Vorprüfung und Konfiguration und Funktionen laden
if [ ! -d "$hauptskriptverzeichnis" ]  
	then
		echo "$(date +%H:%M:%S) - ERROR: Das Hauptskriptverzeichnis wurde nicht gefunden."  
		echo "                  $hauptskriptverzeichnis"  
		exit 1
fi
if [ ! -f "$configdatei" ]  
	then
		echo "$(date +%H:%M:%S) - ERROR: Die Konfig-Datei wurde nicht gefunden."  
		echo "                  $configdatei"  
		exit 1
fi
source "$configdatei" # Konfiguration laden  
if [ ! -f "$functionsdatei" ]  
	then
		echo "$(date +%H:%M:%S) - ERROR: Das Hilfskript mit Funktionen wurde nicht gefunden."  
		echo "                  $functionsdatei"  
		exit 1
fi
source "$functionsdatei" # Funktionen laden  
if [ ! -f "$workerskriptdatei" ]  
	then
		echo "$(date +%H:%M:%S) - ERROR: Das Hilfskript zur Jobabarbeitung wurde nicht gefunden."  
		echo "                  $workerskriptdatei"  
		exit 1
fi
if [ ! -d "$logverzeichnis" ]  
	then
		echo "$(date +%H:%M:%S) - ERROR: Das Log-Hauptverzeichnis wurde nicht gefunden."  
		echo "                  $logverzeichnis"  
		exit 1
fi
if [ ! -d "$lokalequelle" ]  
	then
		echo "$(date +%H:%M:%S) - ERROR: Das lokale Verzeichnis zum Mounten der Quellen wurde nicht gefunden."  
		echo "                  $lokalequelle"  
		exit 1
fi
if [ ! -d "$jobskriptverzeichnis" ]  
	then
		echo "$(date +%H:%M:%S) - ERROR: Das Verzeichnis der Job-Skripte wurde nicht gefunden."  
		echo "                  $jobskriptverzeichnis"  
		exit 1
fi


# Variablen vorbelegen (hier nichts ändern)
jobanzahl=0
joberfolgreichanzahl=0
jobmitfehleranzahl=0
fehleraufgetreten=0
gesamtbytes=0

# Startzeit merken
zeitformat="+%Y-%m-%d %H:%M:%S" # Format in Variable speichern wegen enthaltenem Leerzeichen  
gesamtstartzeit="$(date "$zeitformat")"  

# Verzeichnis für Logdateien anlegen (-p = kein Fehler wenn Verzeichnis schon vorhanden)
startdatum="$(date +%Y-%m-%d)"  
mkdir -p -m 0755 ${logverzeichnis}/${startdatum}/

# Master-Logdatei anlegen
datum="$(date +%Y-%m-%d_%H-%M-%S)"  
masterlogdatei="${logverzeichnis}/${startdatum}/${datum}__backup.txt"  
sudo echo "$(date +%H:%M:%S) - INFO: Backup wurde gestartet" | sudo tee $masterlogdatei # Nur hier ohne append  
sudo echo "$(date +%H:%M:%S) - INFO: Logstammverzeichnis: $logverzeichnis" | sudo tee --append $masterlogdatei  
sudo echo "$(date +%H:%M:%S) - INFO: Logunterverzeichnis: $startdatum" | sudo tee --append $masterlogdatei  

# Prüfen ob eine andere Instanz des Skripts noch läuft
if [ -f "${hauptskriptverzeichnis}/$isrunningdatei" ]  
	then
		sudo echo "$(date +%H:%M:%S) - ERROR: Eine andere Backup-Instanz läuft noch. Die neue Instanz wird beendet." | sudo tee --append $masterlogdatei  
		fehleraufgetreten=1
		ende 2 # $isrunningdatei nicht löschen daher ende 2
	else
		touch "${hauptskriptverzeichnis}/isrunning.txt"  
fi

# Die "isrunningdatei" erzeugen 
touch "${hauptskriptverzeichnis}/isrunning.txt"  
if [ -f "${hauptskriptverzeichnis}/$isrunningdatei" ]  
	then
		sudo chmod a+rwx "${hauptskriptverzeichnis}/$isrunningdatei"  
		fehlercode=$?
		if [ ! $fehlercode -eq 0 ]
			then
				sudo echo "$(date +%H:%M:%S) - ERROR: Fehler beim Setzen der Rechte für die isrunningdatei" | sudo tee --append $masterlogdatei  
				fehleraufgetreten=1
		fi
	else
		sudo echo "$(date +%H:%M:%S) - ERROR: Die isrunningdatei wurde nicht erzeugt" | sudo tee --append $masterlogdatei  
		fehleraufgetreten=1
fi

# Prüfen ob zumindest 1 Job-Skript existiert
jobgefunden=`find $jobskriptverzeichnis/$jobskriptnamensmuster -type f`
if [ "$jobgefunden" = "" ]  
	then
		sudo echo "$(date +%H:%M:%S) - ERROR: Keinen Job gefunden, Backup wird beendet" | sudo tee --append $masterlogdatei  
		fehleraufgetreten=1
		ende 1
fi

# Job-Skripte suchen und in Array speichern
skripte=()
for datei in "$jobskriptverzeichnis/$jobskriptnamensmuster"  
do 
	skripte+=($datei)
done
jobanzahl=${#skripte[@]}

# Alle gefundenen Job-Skripte auflisten
for s in "${skripte[@]}"  
do
	source "${s}"  
	sudo echo "$(date +%H:%M:%S) - INFO: Job gefunden: ${logtitel} (${logprefix})" | sudo tee --append $masterlogdatei  
done
sudo echo "$(date +%H:%M:%S) - INFO: Jobanzahl: ${jobanzahl}" | sudo tee --append $masterlogdatei  
sudo echo "" | sudo tee --append $masterlogdatei  

# Quelle eventuell unmounten, für den Fall dass es nicht erledigt ist
unmounten $lokalequelle $testdatei # function unmounten() aufrufen

# Alle gefundenen Job-Skripte nacheinander ausführen
for s in "${skripte[@]}"  
do
	jobstartzeit="$(date "$zeitformat")"  
	returnwert=0
	neuedateien=0
	neueverzeichnisse=0
	neuedaten=0
	rsyncoptionen=$rsyncstandardoptionen # rsyncoptionen werden eventuell gleich durch Job-Skript geändert
	versionenanzahl=$versionenstandardanzahl # versionenanzahl wird eventuell gleich durch Job-Skript geändert
	source "${s}"  
	sudo echo "$(date +%H:%M:%S) - INFO: Job wird gestartet: ${logtitel} (${logprefix})" | sudo tee --append $masterlogdatei  
	source "${hauptskriptverzeichnis}/psync.sh"  
	returnwert=$?

	if [ $returnwert -eq 0 ]
		then
			sudo echo "$(date +%H:%M:%S) - INFO: Job wurde beendet : Keine Fehler aufgetreten" | sudo tee --append $masterlogdatei  
			joberfolgreichanzahl=$(($joberfolgreichanzahl + 1))
		else
			if [ -f $scriptlogdatei ]
				then
					sudo echo "$(date +%H:%M:%S) - ERROR: Fehler siehe     : $scriptlogdateiname" | sudo tee --append $masterlogdatei  
				else
					sudo echo "$(date +%H:%M:%S) - ERROR: Fehler siehe     : (kein Skript-Log vorhanden)" | sudo tee --append $masterlogdatei  
			fi
			if [ -f $rsynclogdatei ]
				then
					sudo echo "                                   : $rsynclogdateiname" | sudo tee --append $masterlogdatei  
				else
					sudo echo "                                   : (kein rsync-Log vorhanden)" | sudo tee --append $masterlogdatei  
			fi
			jobmitfehleranzahl=$(($jobmitfehleranzahl + 1))
			fehleraufgetreten=1
	fi
	sudo echo "$(date +%H:%M:%S) - INFO: Dateien/Ver./Daten: $neuedateien / $neueverzeichnisse / $neuedaten" | sudo tee --append $masterlogdatei  

	jobbeginnunix=`date --utc --date "$jobstartzeit" +%s`  
	jobendezeit="$(date "$zeitformat")"  
	jobendeunix=`date --utc --date "$jobendezeit" +%s`  
	jobsekunden=$((jobendeunix-jobbeginnunix))
	jobdauer="$((jobsekunden /3600)) Std. $((jobsekunden % 3600 /60)) Min. $((jobsekunden % 60)) Sek."  
	sudo echo "$(date +%H:%M:%S) - INFO: Job benötigte     : $jobdauer" | sudo tee --append $masterlogdatei  
	sudo echo "" | sudo tee --append $masterlogdatei  
	unmounten $lokalequelle $testdatei # für den Fall dass es nicht erledigt ist
done

ende $fehleraufgetreten # function ende () mit 0 oder 1 aufrufen

2. Konfiguration konfiguration.sh
#!/bin/bash

# Konfiguration
jobskriptverzeichnis="/volume1/admin/scripte/backup/jobs"  
jobskriptnamensmuster="sichern_*.sh"  
rsyncstandardoptionen="-rtlDW --stats" ## h und hh (human readable) nicht verwenden, da die Auswertung auf bytes basiert  
versionenstandardanzahl=7 # 7 Verzeichnisse (0-6) werden verwendet
logverzeichnis="/volume1/admin/scripte/backup/logs"  
lokalequelle="/volume1/admin/scripte/backup/mount"  
testdatei="${lokalequelle}/lokal.txt" # Die Testdatei muss in diesem Verzeichnis existieren  
isrunningdatei="isrunning.txt"  
windowsbenutzername="Administrator"  
mailepfaenger=ich@example.com
mailsenden=1

3. Funktionen funktionen.sh
#!/bin/bash

function unmounten() # Das Quellverzeichnis unmounten. Aufruf: unmounten $lokalequelle $testdatei
{
	# $1=$lokalequelle, $2=$testdatei
	if [ ! -f $2 ]
		then
		sudo umount $1
	fi
	fehlercode=$?
	if [ ! $fehlercode -eq 0 ]
		then
			sudo echo "$(date +%H:%M:%S) - ERROR: Fehler $fehlercode beim Unmounten der lokalen Quelle" | sudo tee --append $masterlogdatei  
			fehleraufgetreten=1
			ende 1
	fi
}

function leertest() # Prüfen, ob ein Verzeichnis leer ist. Falls das Verzeichnis nicht existiert gilt es auch als leer
{
	# Syntax: if leertest Pfad then echo "leer" else echo "nicht leer" fi 
	if [ $(ls -a "$1" | wc -l) -gt 2 ] # wc = word count (l = lines = zeilen) = 2 (bei . und ..)  
		then
			return 1 # nicht leer
		else
			return 0 # leer
	fi
}

function verzeichnisrotation() # Bei z.B. versionsanzahl=7 die Verzeichnisse 0-6 rotieren: 6 löschen, 0-5 umbenennen in 1-6 und 0 neu anlegen
{
	maxversion=$(($versionenanzahl - 1))
	maxverzeichnis=-1 # Vorbelegenung für höchster Verzeichnisname ohne Lücke (0,1,2,5 => 2)
	fehlertext=""  
	for ((i=0;i<=$maxversion;i++))
	do
		if [ -d "${1}/${i}" ] # existiert das Verzeichnis (0..maxversion)  
			then
				if leertest ${1}/${i}
					then
						fehlertext="$(date +%H:%M:%S) - ERROR: Das alte Verzeichnis $i ist leer."  
						return 1
				fi
				maxverzeichnis=$i # wird jedesmal eins höher
			else
				j=$i
				while [[ $((++j)) -le $maxversion ]] # prüfen ob danach noch Verzeichnisse existieren
				do
					if [ -d "${1}/${j}" ]  
						then
							if [ "$maxverzeichnis" == "-1" ]  
								then
									fehlertext="$(date +%H:%M:%S) - ERROR: Die Verzeichnissequenz weist eine Lücke bei Verzeichnis 0 auf."  
								else
									fehlertext="$(date +%H:%M:%S) - ERROR: Die Verzeichnissequenz weist eine Lücke nach Verzeichnis $maxverzeichnis auf."  
							fi
							return 1
					fi
				done
				break
		fi
	done
	# zuvor keine Lücken in der Verzeichnissequenz 0-aufsteigend gefunden oder 0..maxversion nicht vorhanden
	# Zuvor keine leeren Verzeichnisse in der Verzeichnissequenz gefunden oder 0..n nicht vorhanden

	# Verzeichnis maxversion löschen, falls vorhanden
	if [ -d "${1}/$maxversion" ]  
		then
			sudo /opt/bin/rm -rf ${1}/${maxversion}/
	fi
	fehlercode=$?
	if [ ! $fehlercode -eq 0 ]
		then
			fehlertext="$(date +%H:%M:%S) - ERROR: Fehler $fehlercode beim Löschen von Verzeichnis ${maxversion}"  
			return 1
	fi

	# Verzeichnisnamen 0..(maxversion-1) um 1 erhöhen
	j=$versionenanzahl
	startwert=$(($maxversion - 1))
	for ((i=$startwert;i>=0;i--)) # maxversion..0
	do
		j=$(( $j - 1 ))
		if [ -d "${1}/${i}" ]  
			then
				sudo mv ${1}/${i}/ ${1}/${j}/
		fi
		fehlercode=$?
		if [ ! $fehlercode -eq 0 ]
			then
				fehlertext="$(date +%H:%M:%S) - ERROR: Fehler $fehlercode beim Umbenennen von Verzeichnis $i"  
			return 1
		fi
	done

	# Verzeichnisnamen 0 neu erstellen
	sudo mkdir -m 0755 ${1}/0/
	fehlercode=$?
	if [ ! $fehlercode -eq 0 ]
		then
			fehlertext="$(date +%H:%M:%S) - ERROR: Fehler $fehlercode beim Erstellen von Verzeichnis 0"  
			return 1
	fi

	return 0
}

function rysnclog_auswerten() # rsynclog durchsuchen und ein paar Werte auslesen
{
	#Varianten der relevanten Log-Zeile für kopierte Dateien/Verzeichnisse:
	#2018/01/13 15:28:21 [1403] Number of created files: 0
	#2018/01/13 15:27:32 [1900] Number of created files: 10 (reg: 10)
	#2018/01/13 15:27:54 [1084] Number of created files: 2 (dir: 2)
	#2018/01/13 15:28:15 [1246] Number of created files: 12 (reg: 10, dir: 2)

	#Varianten der relevanten Log-Zeile für kopierte Datenmenge:
	#2018/01/13 15:54:07 [4475] Total transferred file size: 826 bytes
	#2018/01/13 15:54:07 [4475] Total transferred file size: 2.64K bytes
	#2018/01/13 15:54:07 [4475] Total transferred file size: 2.64M bytes
	#2018/01/13 15:54:07 [4475] Total transferred file size: 2.64G bytes
	#2018/01/13 15:54:07 [4475] Total transferred file size: 2.64T bytes
	neueverzeichnisse=0
	neuedateien=0
	neuedaten=0
	if [ ! -f $rsynclogdatei ]
		then return 1
	fi
	
	while read line
	do
		if [[ $line == *"Number of created files:"* ]]  
			then
				ar=($line) # Zeile an Leerzeichen splitten
				if [ "${ar[7]}" != "0" ]  
					then
						if [ "${ar[8]}" == "(reg:" ]  
							then
								neuedateien=${ar[9]:0:-1}
						fi
						if [ "${ar[8]}" == "(dir:" ]  
							then
								neueverzeichnisse=${ar[9]:0:-1}
						fi
						if [ "${ar[10]}" == "dir:" ]  
							then
								neueverzeichnisse=${ar[11]:0:-1}
						fi
				fi
				neuedateien=${neuedateien//,/.} # alle Tausendertrennzeichen austauschen
				neueverzeichnisse=${neueverzeichnisse//,/.} # alle Tausendertrennzeichen austauschen
		fi
		if [[ $line == *"Total transferred file size:"* ]]  
			then
				ar=($line) # Zeile an Leerzeichen splitten
				byteszuhuman ${ar[7]} # function byteszuhuman () aufrufen
				fehlercode=$? # returnwert der Funktion
				if [ ! $fehlercode -eq 0 ]
					then
						echo "$(date +%H:%M:%S) - ERROR: Fehler bei der Umrechung der Bytes" | sudo tee --append $scriptlogdatei  
					else
						neuedaten=$ergebnisumgerechnet
						gesamtbytes=$(( $gesamtbytes + $ergebnisinbytes )) # Aufaddieren für Gesamtergebnis aller Jobs
				fi
		fi
	done < $rsynclogdatei

	return $?
}

function byteszuhuman () # Umrechnung von z.B. 1,024 (bytes) in 1,00 kB (eintausend und 24 in 1 Komma 00), die Variable $ergebnisumgerechnet wird belegt
{
	bytes=$1
	bytes=${bytes//,/} # alle Tausendertrennzeichen entfernen
	ergebnisinbytes=$bytes
	ergebnisumgerechnet=0

	# Prüfung
	if [ ! "${bytes//[0-9]}" = "" ] || [ "$bytes" = "" ] # bleibt was übrig wenn alle Ziffern entfernt sind oder war es leer?  
		then
			ergebnis=0
			return 1
	fi

	# Unterscheidung
	if ((0<=$bytes && $bytes<1024))
		then
			menge=$bytes
			einheit="Byte"  
	elif ((1024<=$bytes && $bytes<1048576))
		then
			menge=`echo $bytes | awk '{ byte = $1 /1024 ; print byte }'`  
			einheit="kB"  
	elif ((1048576<=$bytes && $bytes<1073741824))
		then
			menge=`echo $bytes | awk '{ byte = $1 /1024/1024 ; print byte }'`  
			einheit="MB"  
	elif ((1073741824<=$bytes && $bytes<1099511627776))
		then
			menge=`echo $bytes | awk '{ byte = $1 /1024/1024/1024 ; print byte }'`  
			einheit="GB"  
	else
		menge=`echo $bytes | awk '{ byte = $1 /1024/1024/1024/1024 ; print byte }'`  
		einheit="TB"  
	fi

	# Formatierung
	if [ "$einheit" = "Byte" ]  
		then
			ergebnisumgerechnet=`printf "%'.0f\n" $menge` # Mit Tausendertrennzeichen ohne Nachkommastellen  
		else
			ergebnisumgerechnet=`printf "%'.2f\n" $menge` # Mit Tausendertrennzeichen und zwei Nachkommastellen  
	fi

	# Nachformatierung
	ergebnisumgerechnet=${ergebnisumgerechnet//,/x} # alle Tausendertrennzeichen austauschen
	ergebnisumgerechnet=${ergebnisumgerechnet/./,}  # das  Dezimaltrennzeichen austauschen
	ergebnisumgerechnet=${ergebnisumgerechnet//x/.} # alle Tausendertrennzeichen austauschen
	ergebnisumgerechnet="$ergebnisumgerechnet $einheit"  
	return 0
}

function ende() # Aufräumem, Statistik, E-Mailversand, Beenden
{
	status="$1" # der Status, der an ende() übergeben wurde($1)  

	# isrunningdatei löschen (oder nicht)
	if [ ! $status -eq 2 ]
		then
			# isrunningdatei löschen
			if [ -f ${hauptskriptverzeichnis}/$isrunningdatei ]
				then
					sudo rm -f ${hauptskriptverzeichnis}/$isrunningdatei
			fi
			if [ -f ${hauptskriptverzeichnis}/$isrunningdatei ] # konnte offensichtlich nicht gelöscht werden
				then
					sudo echo "$(date +%H:%M:%S) - ERROR: Datei $isrunningdatei konnte nicht gelöscht werden" | sudo tee --append $masterlogdatei  
					fehleraufgetreten=1
			fi
	fi

	# Ordner für Logdateien an Windowsbenutzer übergeben
	sudo chown -R ${windowsbenutzername}.users ${logverzeichnis}/${startdatum}
	fehlercode=$?
	if [ ! $fehlercode -eq 0 ]
		then
			sudo echo "$(date +%H:%M:%S) - ERROR: Fehler beim Übergeben des Besitzes am Logordner ${startdatum}" | sudo tee --append $masterlogdatei  
			fehleraufgetreten=1
	fi
	# Rechte für Logordner und Logdateien setzen
	sudo chmod a+rwx -R ${logverzeichnis}/${startdatum}
	fehlercode=$?
	if [ ! $fehlercode -eq 0 ]
		then
			sudo echo "$(date +%H:%M:%S) - ERROR: Fehler beim Setzen der Rechte für Logordner/-dateien ${startdatum}" | sudo tee --append $masterlogdatei  
			fehleraufgetreten=1
	fi

	# Gesamtdatenmenge ausgeben
	byteszuhuman $gesamtbytes # function byteszuhuman () aufrufen
	fehlercode=$? # returnwert der Funktion
	if [ ! $fehlercode -eq 0 ]
		then
			echo "$(date +%H:%M:%S) - ERROR: Die Gesamtdatenmenge konnte nicht umgerechnet werden" | sudo tee --append $masterlogdatei  
		else
			echo "$(date +%H:%M:%S) - INFO: Backup kopierte $ergebnisumgerechnet" | sudo tee --append $masterlogdatei  
	fi

	# Gesamtlaufzeit ausgeben
	gesamtbeginnunix=`date --utc --date "$gesamtstartzeit" +%s`  
	gesamtendezeit="$(date "$zeitformat")"  
	gesamtendeunix=`date --utc --date "$gesamtendezeit" +%s`  
	gesamtsekunden=$((gesamtendeunix - gesamtbeginnunix))
	gesamtdauer="$((gesamtsekunden / 3600)) Std. $((gesamtsekunden % 3600 / 60)) Min. $((gesamtsekunden % 60)) Sek."  
	sudo echo "$(date +%H:%M:%S) - INFO: Backup lief $gesamtdauer" | sudo tee --append $masterlogdatei  

	# Transferrate ausgeben
	transfersekunden=$gesamtsekunden
	if [ $transfersekunden -eq 0 ] # geteilt durch 0 vermeiden
		then
			transfersekunden=1
	fi
	transferrate=$(($gesamtbytes / $transfersekunden))
	byteszuhuman $transferrate # function byteszuhuman () aufrufen
	transferrate=$ergebnisumgerechnet
	sudo echo "$(date +%H:%M:%S) - INFO: Gesamtübertragungsrate: ${transferrate}/s" | sudo tee --append $masterlogdatei  

	# Abschlussstatus ausgeben und "mailbetreffteil" belegen 
	if [ $fehleraufgetreten -eq 0 ] && [ $status -eq 0 ]
		then
			sudo echo "$(date +%H:%M:%S) - INFO: Backup erfolgreich beendet" | sudo tee --append $masterlogdatei  
			mailbetreffteil="erfolgreich"  
		else
			sudo echo "$(date +%H:%M:%S) - ERROR: Backup mit Fehler beendet" | sudo tee --append $masterlogdatei  
			if [ $jobmitfehleranzahl -eq 1 ]
				then
					mailbetreffteil="abgeschlossen, 1 Job mit Fehler"  
				else
					mailbetreffteil="abgeschlossen, $jobmitfehleranzahl Jobs mit Fehler"  
			fi
	fi

	# E-Mail senden ?
	if [ $mailsenden -ne 1 ] || [ "$mailepfaenger" == "" ]  
		then
			exit $status
	fi

	# E-Mail senden !
	if [ $jobanzahl -eq 1 ] # Job oder Jobs (Einzahl oder Mehrzahl)
		then
			job_s=""  
		else
			job_s="s"  
	fi
	if [ $jobanzahl -eq 0 ]
		then
			mailbetreff="Nas-Backup: Keinen Job gefunden ($(date +%d.%m.%Y) um $(date +%H:%M:%S))"  
		else
			mailbetreff="Nas-Backup: ${jobanzahl} Job$job_s $mailbetreffteil ($(date +%d.%m.%Y) um $(date +%H:%M:%S))"  
	fi
	if [ $status -eq 2 ]
		then
			mailbetreff="Nas-Backup: Ein laufendes Backup hat die Ausführung verhindert ($(date +%d.%m.%Y) um $(date +%H:%M:%S))"  
	fi

	#echo "Nachrichtentext"  | /opt/bin/nail -s "${mailbetreff}" -a "${masterlogdatei}" $mailepfaenger 
	echo ""  | /opt/bin/nail -s "${mailbetreff}" -a "${masterlogdatei}" $mailepfaenger  

	exit $status
}

4. Workerskript psync.su
#!/bin/bash

### Workerscript zur Jobabarbeitung ###
  # Nicht direkt ausführen, es wird vom Backup-Skript für jeden Job aufgerufen

fullbackup=true

# Logdatei erzeugen
datum="$(date +%Y-%m-%d_%H-%M-%S)"  
scriptlogdateiname="${datum}_${logprefix}_job.txt"  
scriptlogdatei="${logverzeichnis}/${startdatum}/${scriptlogdateiname}"  
rsynclogdateiname="${datum}_${logprefix}_rsync.txt"  
rsynclogdatei="${logverzeichnis}/${startdatum}/${rsynclogdateiname}"  
echo "$(date +%H:%M:%S) - INFO: Starte Backupjob von $logtitel" | sudo tee --append $scriptlogdatei # Nur hier ohne append  

# Prüfen ob der Zielordner existiert
if [ ! -d "${lokalesziel}" ]  
	then
		sudo echo "$(date +%H:%M:%S) - ERROR: Der Zielordner existiert nicht: ${lokalesziel}" | sudo tee --append $scriptlogdatei  
		return 1
fi

# Quelle mounten
echo "$(date +%H:%M:%S) - INFO: Windowsquelle verbinden..." | sudo tee --append $scriptlogdatei  
if [ ! -f $testdatei ]
	then
		echo "$(date +%H:%M:%S) - ERROR: Die Testdatei existiert nicht im lokalen Ordner." | sudo tee --append $scriptlogdatei  
		return 1
fi
sudo mount.cifs -o iocharset=utf8,ro,username=${benutzername},password=${kennwort} "$windowsquelle" $lokalequelle  
if [ -f $testdatei ]
	then
		echo "$(date +%H:%M:%S) - ERROR: Das Mounten hat nicht funktioniert." | sudo tee --append $scriptlogdatei  
		return 1
fi

# Prüfen ob 0 schon existiert
if [ -d "${lokalesziel}/0" ]  
	then
		fullbackup="false"  
fi

# Verzeichnisrotation ausführen, Verzeichnis 0 wird dabei angelegt
verzeichnisrotation ${lokalesziel} # function verzeichnisrotation () wird aufgerufen
fehlercode=$? # returnwert der function
if [ ! $fehlercode -eq 0 ]
	then
		echo "$(date +%H:%M:%S) - ERROR: Bei der Verzeichnisrotation trag ein Fehler auf..." | sudo tee --append $scriptlogdatei  
		echo "$fehlertext" | sudo tee --append $scriptlogdatei  
		return 1
fi

# Dateien kopieren
if [ "$fullbackup" == "true" ]  
	then
		echo "$(date +%H:%M:%S) - INFO: Vollsicherung: $windowsquelle -> ${lokalesziel}/0 ..." | sudo tee --append $scriptlogdatei  
		echo "/opt/bin/rsync $rsyncoptionen ${lokalequelle}/ ${lokalesziel}/0" | sudo tee $rsynclogdatei # Nur hier ohne append  
		sudo /opt/bin/rsync $rsyncoptionen --log-file="$rsynclogdatei" ${lokalequelle}/ ${lokalesziel}/0  
	else
		echo "$(date +%H:%M:%S) - INFO: Inkrementelle Sicherung: $windowsquelle -> ${lokalesziel}/0 ..." | sudo tee --append $scriptlogdatei  
		echo "/opt/bin/rsync $rsyncoptionen --link-dest=${lokalesziel}/1 ${lokalequelle}/ ${lokalesziel}/0" | sudo tee $rsynclogdatei # Nur hier ohne append  
		sudo /opt/bin/rsync $rsyncoptionen --log-file="$rsynclogdatei" --link-dest=${lokalesziel}/1 ${lokalequelle}/ ${lokalesziel}/0  
fi  
backupfehler=$?
rysnclog_auswerten # function rysnc_auswerten() aufrufen
fehlercode=$? # returnwert der function
if [ ! $fehlercode -eq 0 ]
	then
		echo "$(date +%H:%M:%S) - ERROR: Kopierstatistik nicht verfügbar" | sudo tee --append $scriptlogdatei  
		neueverzeichnisse="?"  
		neuedateien="?"  
		neuedaten="?"  
	else
		if [ ! $neueverzeichnisse  -eq 1 ]
			then
				echo "$(date +%H:%M:%S) - INFO: Kopiert: $neueverzeichnisse Verzeichnisse" | sudo tee --append $scriptlogdatei  
			else
				echo "$(date +%H:%M:%S) - INFO: Kopiert: $neueverzeichnisse Verzeichnis" | sudo tee --append $scriptlogdatei  
		fi
		if [ ! $neuedateien  -eq 1 ]
			then
				echo "                          $neuedateien Dateien" | sudo tee --append $scriptlogdatei  
			else
				echo "                          $neuedateien Datei" | sudo tee --append $scriptlogdatei  
		fi
		echo "                          $neuedaten" | sudo tee --append $scriptlogdatei  
fi
if [ $backupfehler -eq 0 ] || [ $backupfehler -eq 24 ]
	then
		echo "$(date +%H:%M:%S) - INFO: Das Kopieren wurde erfolgreich beendet." | sudo tee --append $scriptlogdatei  
	else
		echo "$(date +%H:%M:%S) - ERROR: Beim Kopieren trat der Fehler $backupfehler auf." | sudo tee --append $scriptlogdatei  
fi

# Zeitstempel nach Backupjob aktualisieren
sudo touch ${lokalesziel}/0/
fehlercode=$?
if [ ! $fehlercode -eq 0 ]
	then
		echo "$(date +%H:%M:%S) - ERROR: Der Zeitstempel von Verz. 0 konnte nicht aktualisiert werden." | sudo tee --append $scriptlogdatei  
fi

# Besitz an "Windows"-Benutzer.users übergeben 
sudo chown -R ${benutzername}.users ${lokalesziel}/0
fehlercode=$?
if [ ! $fehlercode -eq 0 ]
	then
		echo "$(date +%H:%M:%S) - ERROR: Der Besitz für Verz. 0 konnte nicht geändert werden." | sudo tee --append $scriptlogdatei  
fi

# Quelle unmounten
echo "$(date +%H:%M:%S) - INFO: Windowsquelle trennen..." | sudo tee --append $scriptlogdatei  
sudo umount $lokalequelle
fehlercode=$?
if [ ! $fehlercode -eq 0 ]
	then
		echo "$(date +%H:%M:%S) - ERROR: Das Unmounten hat nicht funktioniert. Fehler: $fehlercode" | sudo tee --append $scriptlogdatei  
fi

# Ende
if [ $backupfehler -eq 0 ]
	then
		echo "$(date +%H:%M:%S) - INFO: Backupjob erfolgreich abgeschlossen." | sudo tee --append $scriptlogdatei  
		return 0
	else
		echo "$(date +%H:%M:%S) - INFO: Backupjob mit Fehlercode $backupfehler abgeschlossen." | sudo tee --append $scriptlogdatei  
		return 1
fi

return 0

5. Aufgabenskripte sichern_eine-aufgabe.su (und beliebig weitere)
#!/bin/bash

# Konfiguration

## Windows
benutzername="Administrator"  
kennwort="geheim"  
windowsquelle="//192.168.0.15/Freigabe/Dokumentenordner/" # mit abschließenden Slash  

## Linux
lokalesziel="/volume1/Backups/ServerA/Dokumente" # ohne abschließenden Slash  

## Log
logtitel="Server A - Dokumente" # wahlfrei, wird nur in den Loginhalten verwendet  
logprefix="server-a-dokumente" # wahlfrei, wird als Teil des Dateinamens der jobspezifischen Logdateien verwendet  

## Backup (nur wenn vom konfigurierten Standard abweichend)
#rsyncoptionen=
#versionenanzahl=


In funktionen.sh wird das Tool nail zum E-Mail-Versand der Logdatei verwendet.
Nail muss gegebenenfalls noch installiert und konfiguriert werden und der Pfad zu nail angepasst werden.
Betriff: funktionen.sh - Zeile 327

In psync.sh wird das Tool rsync zur eigentlichen Backupausführung verwendet.
rsync muss gegebenenfalls noch installiert werden und der Pfad zu rsync angepasst werden.
Betriff: psync.sh - Zeilen 58 und 62

Gruß Frank

EDIT: Inhalt des Beitrags komplett aktualisiert am 21.01.2018
Pedant
Pedant Jan 21, 2018 at 11:56:30 (UTC)
Goto Top
Hallo,

die Skritpe für meine rsync-Backuplösung habe ich noch weiter optimiert.
Meinen vorhergehenden Beitrag habe ich entsprechend angepasst.
Die Skripte sind dort jetzt so, wie ich sie bei mir im produktiven Einsatz habe.
Falls jemand Interesse an der Lösung hat, könnte er diese Skripte mit wenigen Anpassungen nutzen oder nach belieben "umprogrammieren".

Gruß Frank