Howto: Backupmailbox – ein Kochrezept

Das Problem: ein Backup von wenigen aber wichtigen kleinen Dateien von einem Server machen, aber irgendwie keine Zeit den eigentlichen Backup-Client einzurichten. Sowieso ist in diesem Fall der Backup-Client auf dem Server eigentlich ein bisschen wie mit Kanonen auf Spatzen zu schiessen. Backup muss aber dennoch gemacht werden.

Die Loesung: die Backupmailbox. Auf dem Mailserver wird eine Backup-Aliasmailadresse eingerichtet. Backups gehen dann per Mail an diese Adresse, ein Skript extrahiert die Anhaenge und legt sie im Dateisystem in einer Ordnerstruktur ab. Diese Ordner werden dann ueber den Backupclient – der auf dem Mailserver sowieso laeuft – mit ins regulaere Backup geschoben. Die Anhaenge werden in Unterordner gespeichert, die dem Namen der EMailadresse entsprechen um sie besser auseinanderzuhalten. Dabei wird das @ gegen einen . ersetzt. Weiter werden nur Emails bearbeitet, die als Betreff ein definiertes Zufallspasswort haben.

Die Umsetzung: Man benoetigt einen Email-Alias, ein Skript, zwei Ordner im Dateisystem, ein Zufallspasswort und 15min Zeit eines Systemadministrators.

Zuerst lege man in der /etc/aliases einen neuen Backup-Alias an:

backupalias: "| /path/to/attatchExtract.sh"

und uebernehme diesen mit dem Kommando newaliases. Danach lege man unter dem definierten Pfad das folgende Skript ab:

#!/bin/bash
 
## base path where attachments and logfile should be stored without trailing slash
BASEPATH=/var/local/backup/
 
 
## The subject needs to match this string, otherwise the mail is dropped without action
SECRETSUBJECT="12345"
 
 
## uniq filename for temp file
FILENAME=$(date +%s)
echo "Filename = $FILENAME" >> $BASEPATH/logfile
 
 
## safe email to tmp file
/bin/cat > /tmp/$FILENAME
 
 
## extract sender from email
SENDER=$(grep "From:" /tmp/$FILENAME | sed 's/.*< *//;s/ *>.*//' | tr "@" ".")
echo "Sender = $SENDER" >> $BASEPATH/logfile
 
 
## extract subject from email
SUBJECT=$(grep "Subject:" /tmp/$FILENAME | sed '/^Subject: */!d; s///; q')
echo "Subject = $SUBJECT" >> $BASEPATH/logfile
 
 
## only do something if secret subject is matched
if [ "$SUBJECT" == "$SECRETSUBJECT" ]; then
 
 
  ## create folder for email address if doesn't exist
  if [ ! -d "$BASEPATH/$SENDER" ] ; then
    echo "Directory $BASEPATH/$SENDER does not exist, creating it" >> $BASEPATH/logfile
    mkdir -p $BASEPATH/$SENDER
  fi
 
 
  ## extract files from email and safe to correcet folder
  /usr/bin/uudeview -i -a -p $BASEPATH/$SENDER/ /tmp/$FILENAME >> $BASEPATH/logfile
  echo >> /$BASEPATH/logfile
 
 
## otherwise just do nothing
else
  echo "Subject didn't match secret Subject. Doing nothing..." >> $BASEPATH/logfile
fi
 
 
## delete tmp file
/bin/rm /tmp/$FILENAME

In dem Skript passe man nun das Zufallspasswort gegen ein eigenes an (makepasswd –chars 40) und aktualisiere den BASEPATH. Zuletzt noch das Skript mit chmod 755 ausfuehrbar machen und das Ergebnis aus dem Mailclient seiner Wahl testen.

Sollte es zu Problemen kommen, dass keine Logdatei und Anhaenge abgelegt werden liegt dieses meistens an fehlenden Rechten.

Bash: Summe von Integers berechnen

Ich stand gerade wieder einmal vor dem Problem, dass ich aus einer Logdatei Meldungen nach dem Muster:

        582 media files copied.

extrahieren und davon dann nur die Zahlen zusammenzaehlen wollte. Ein gutes Beispiel um mal ein paar Tools fuer das Arbeiten mit Texten in der Shell vorzustellen.

Als erstes moechte ich alle Zeilen extrahieren. Dafuer nutze ich das Kommando grep:

grep "media files copied." /tmp/logfile

Ich erhalte daraufhin eine Liste mit allen Zeilen, die diesen String enthalten. Als naechstes moechte ich daraus die Nummern extrahieren. Der String beginnt immer mit acht Leerzeichen, und danach steht die Nummer. Hierfuer nutze ich das Kommendo cut, das mir einen String an bestimmten Trennzeichen aufteilt und mir davon die angegebene Spalte ausgibt. Als Trennzeichen eignet sich hier das Leerzeichen, da vor und nach der Nummer eines steht. Da acht Leerzeichen am Anfang sind, brauche ich die neunte Spalte. Dieses erreiche ich mit dem folgenden Code:

grep "media files copied." /tmp/logfile | cut -d " " -f 9

Ich leite also mit der Pipe | die Ausgabe des grep Kommandos weiter zu cut. Das Ergebnis ist eine Liste mit vielen Zahlen, die jede fuer sich in einer eigenen Zeile stehen. Um diese nun mit Hilfe des Kommandos bc zu addieren, muss daraus noch eine Rechenaufgabe werden. Ich greife dafuer auf das Programm paste zurueck. Hiermit kann ich Zeilen zusammenfuehren und dabei den Zeilenumbruch gegen ein von mir vorgegebenes Zeichen ersetzen. In diesem Fall moechte ich, dass jede Zeile nacheineander (seriell, nicht parallel) abgearbeitet wird, und der Zeilenumbruch gegen ein „+“ Zeichen ersetzt wird. Dementsprechend sieht mein Aufruf nun wie folgt aus:

grep "media files copied." /tmp/logfile | cut -d " " -f 9 | paste -sd+

Heraus kommt bei mir gerade das:

32+32+52+32+187+208+22+793+63+632+8+16+40+293+313+48+243+96+32+579+647+768+808+18+48+102+120+258+52+18+52+52+10+52+108+137+512+48+40+48+51+96+582+52+687+101+696+48+48+784+766+776+614+599+804+32+28+779+76+32+86+99+20+34+776+837+44+824+28+803+48

was ich nun nur noch zu bc pipen muss, und schon weiss ich, dass:

grep "media files copied." /tmp/logfile | cut -d " " -f 9 | paste -sd+  | bc

18869 Dateien kopiert wurden.

Bash: find broken symlinks in file system

Befehl:

find /path/to/folder -type l | (while read broken; do test -e "$broken" || ls -ld "$broken"; done)

Uebersetzung:
Suche mir alle Dateien vom Typ „Link“ im Verzeichnis /path/to/folder inkl. allen Unterverzeichnissen und gib mir den kompletten Pfad zu den Links aus. Die Ausgabe wird in eine WHILE-Schleife gepipt die diese zeilenweise einliest. Fuer jede Zeile wird einmal getestet ob die Datei existiert. Wenn die Datei existiert isses dufte, wenn nicht gib den defekten Link aus.