Eigentlich geht es hier nicht speziell um Webserver-Backups, sondern eher allgemein um die Verwendung des sftp-Kommandozeilenprogrammes.

In meinem ganz speziellen Fall wollte ich jedoch ein wöchentliches Backup aller PHP-Skripte & Co. von einem Webserver machen, da ich an den PHP-Skripten ständig arbeite und im Hintergrund gerne eine relativ aktuelle funktionierende Kopie haben möchte.

Sucht man im Internet zu Hilfestellungen zum sftp-Kommandozeilenprogramm, endet die Hilfestellung meist mit dem Tipp, doch lieber scp oder rsync zu verwenden.
Dies soll hier nicht der Fall sein, da in meinem Fall der betroffene Webserver jegliche Verbindung außer explizit sftp ausschließt.

Mein Ziel:
Ein kurzes Skript, das fast alle Dateien eines Verzeichnisses auf dem SFTP-Server herunterlädt. Ich paar Dateien und Verzeichnisse möchte ich allerdings vom Herunterladen ausschließen, da sie Bilder, Dokumente und temporäre Daten enthalten, welche ich bei meiner Kopie nicht benötige.
Das schließt die einfachste Verwendung von sftp schonmal aus...

Die einfachste Verwendung...

...wäre nämlich, einfach den gesamten Inhalt des SFTP-Servers oder den gesamten Inhalt eines bestimmten Verzeichnisses herunterzuladen:

Alle Daten von wunschordner mit seinen Unterordnern herunterladen:

sftp -r user@webserver:wunschordner /pfad/zu/lokalem/ordner

Nur bestimmte Dateien und Verzeichnisse herunterladen

Voraussetzung:
  • Wir haben eine Datei liste.txt die den Namen alle Dateien und Ordner enthält, welche heruntergeladen werden sollen. Im Folgenden "die Batch-Datei"
  • sshpass ist installiert, um für den Batch-Betrieb das SFTP-Passwort an den SFTP-Server übergeben zu können (wir wollen ja später nicht jedes Mal das Passwort eingeben)

Die Batch-Datei kann wie folgt aussehen:

liste.txt
cd wunschordner
get -r unterordner1
get -r unterordner2
get -r unterordner3
get .htaccess
get .htuser
get index.php

Das hinten angestellte lokale Downloadverzeichnis /pfad/zu/lokalem/ordner aus dem ersten Beispiel hatte bei mir keine Wirkung mehr – es wurde alles in das aktuelle Verzeichnis reingeladen. Deshalb vorangestellt noch ein cd zum Wunsch-Downloadverzeichnis.

cd /pfad/zu/lokalem/ordner
sshpass -p <passwort> sftp -oBatchMode=no -b /pfad/zu/liste.txt user@webserver

Bestimmte Dateien und Verzeichnisse ausschließen

Jetzt wird es komplizierter. Eine --exclude-Funktion, wie es etwa scp anbietet, gibt es hier leider nicht (zumindest hatte ich soetwas nirgends finden können).
Was aber, wenn ich alle Dateien eines Verzeichnisses bis auf zwei bestimmte Dateien herunterladen möchte? Noch dazu, wenn in jenem Verzeichnis mal neue Dateien dazu kommen und ich kein festes Datei-Listing für meine Batach-Datei habe?

Meine Lösung war, sich das aktuelle Dateilisting vom SFTP-Server zu holen und daraus eine Batch-Datei zu erstellen.
Meine Batch-Datei, um das Datei-Listing zu erhalten sieht wie folgt aus:

ls-batch.txt
cd wunschordner
ls -1a

In der Ausgabe, die wir erhalten werden, sind leider auch noch die von sftp ausgeführten Befehle wie sftp> cd wunschordner und die Pfadangaben . und .. enthalten.
Das wollen wir in unserer zu generierenden Batch-Datei nicht haben, deshalb filtere ich dies mit sed raus:

sshpass -p <passwort> sftp -oBatchMode=no -b /pfad/zu/ls-batch.txt user@webserver | sed -E '/^\.$|^\.\.$|^sftp/d'

Ausgabe:
unterordner1
unterordner2
unterordner3
unterordner4
unterordner5
datei1
datei2
datei3
datei4

Nehmen wir an, wir wollen alles, bis auf unterordner3 und datei2 herunterladen.
So nehme ich die beiden Namen zu meiner sed-Regex mit auf.
sed -E '/^\.$|^\.\.$|^sftp|unterordner3|datei2/d'

Nun speichern wir die Ausgabe von sftp direkt in unsere neue Batch-Datei liste.txt

sshpass -p <passwort> sftp -oBatchMode=no -b /pfad/zu/ls-batch.txt user@webserver | sed -E '/^\.$|^\.\.$|^sftp|unterordner3|datei2/d' > /pfad/zu/liste.txt

liste.txt
unterordner1
unterordner2
unterordner4
unterordner5
datei1
datei3
datei4

Vor jeden Eintrag muss nun noch ein get -r vorangestellt werden.
Und befinden sich die Dateien nicht im Datei-Root (wir haben bisher ja immer erst in den Ordner wunschordner gewechselt), müssen wir noch ein cd wunschordner in die erste Zeile stellen.

sshpass -p <passwort> sftp -oBatchMode=no -b /pfad/zu/ls-batch.txt user@webserver | sed -E '/^\.$|^\.\.$|^sftp|unterordner3|datei2/d' | while read line; do echo "get -r $line"; done > /pfad/zu/tmp.txt
echo "cd wunschordner"|cat - /pfad/zu/tmp.txt > /pfad/zu/liste.txt
rm /pfad/zu/tmp.txt

Nun sollte in der Batch-Datei stehen

liste.txt
cd wunschordner
get -r unterordner1
get -r unterordner2
get -r unterordner4
get -r unterordner5
get -r datei1
get -r datei3
get -r datei4

Wir haben zwar unschönerweise auch den Dateien ein Rekursiv -r vorangestellt, aber das stört sftp zum Glück nicht.

Und nun alles zusammen – am Ende packe ich noch alles in ein tar-Archiv, das das aktuelle Datum im Dateinamen hat:

#!/bin/bash

# Liste aller gewünschten Dateien/Verzeichnisse erstellen
sshpass -p <passwort> sftp -oBatchMode=no -b /pfad/zu/ls-batch.txt user@webserver | sed -E '/^\.$|^\.\.$|^sftp|unterordner3|datei2/d' | while read line; do echo "get -r $line"; done > /var/tmp/tmp.txt
echo "cd wunschordner"|cat - /var/tmp/tmp.txt > /pfad/zu/batchliste.txt
rm /var/tmp/tmp.txt

# Download und verpacken
mkdir /var/tmp/mybackup
cd /var/tmp/mybackup
sshpass -p <passwort> sftp -oBatchMode=no -b /pfad/zu/batchliste.txt user@ftp
tar -zcvf /pfad/zu/ordner/$(date +"%Y-%m-%d")-backup.tar.gz /var/tmp/mybackup
rm -r /var/tmp/mybackup