Mit Bash prüfen ob das eigene Script gerade läuft
Die Problemstellung
Ein Script soll alle x Minuten laufen und eine bestimmte Tätigkeit auf dem Server ausführen.
Nun kann es vorkommen, dass das Script aber länger braucht als die Zeitspanne bis zum nächsten Aufruf desselben Scripts (z.B. Kopier und Backup Jobs, Mails abholen, ...)
Natürlich sollte so ein Script dann nicht ein 2tes Mal gestartet werden sondern die Ausführung übersprungen werden. Zu diesem Zweck möchte ich hier ein kleines Bash Script zeigen, das genau diesen Zweck erfüllt.
Die Lösung
#!/bin/bash
#
# testpid.sh - demo script to show how to check if a script
# with the same name is currently running
#
# by Leo Eibler
# resources:
# http://www.eibler.at
# http://leo.sprossenwanne.at
# http://www.nullpointer.at
#
# PID - pid of the current script
PID=$$
# SCRIPTNAME - current name of the script without directory prefix
SCRIPTNAME=`basename $0`
# PIDFILE - where to write the current pid
PIDFILE=/tmp/$SCRIPTNAME.pid
# ENDEXECUTION - if 1 then stop script, if 0 everything is ok and continue
ENDEXECUTION=0
if [ -f "$PIDFILE" ]
then
RUNNINGPID=`cat "$PIDFILE"`
echo "got pid from $RUNNINGPID from pidfile '$PIDFILE'"
PROGRAMPID=`ps -e | grep "$SCRIPTNAME" | grep -v grep | awk '{print $1;}'`
for PIDEL in $PROGRAMPID
do
echo "testing pid of running scripts '$PIDEL' == '$RUNNINGPID' from pidfile"
if [ "$PIDEL" == "$RUNNINGPID" ]
then
echo "found PID $RUNNINGPID current running - end execution"
ENDEXECUTION=1
break
fi
done
fi
if [ "$ENDEXECUTION" == "1" ]
then
echo "Current script '$SCRIPTNAME' already running (pid $RUNNINGPID) - end execution"
exit 1
fi
# writing PID to pidfile
echo $PID > $PIDFILE
#
# ---- START ----
#
echo "do your stuff here ..."
sleep 5
echo "... end script"
#
# ---- END ----
#
# delete pidfile
rm $PIDFILE
exit 0
Die Erklärung
Zuerst holt sich das Script den eigenen Namen mit basename $0 ($0 würde ebenfalls den Pfad des Scriptaufrufs enthalten aber hier würde der spätere Aufruf von ps bzw. das automatische Erstellen und Auslesen des passenden pid-Files scheitern).
Mit dem Namen des Scripts wird dann versucht ein pid-File (welches die Process-ID des aktuell laufenden Scripts enthält) auszulesen. Der Pfad des pid-Files kann beliebig gewählt werden, jedoch muss das Script natürlich Schreibrechte auf die Datei besitzen.
Falls kein pid-File existiert kann das Script davon ausgehen, dass es derzeit nicht bereits läuft und seine eigentliche Arbeit aufnehmen.
Falls jedoch ein pid-File vorhanden ist, wird dieses ausgelesen und mit allen derzeit laufenden Process-IDs von Prozessen mit dem gleichen Namen wie das Script verglichen.
Wird hierbei eine Übereinstimmung gefunden, dann läuft das Script bereits und durch Setzen der Variable $ENDEXECUTION auf 1 wird der Abbruch signalisiert.
Dieser Vergleich mit den Process-IDs von Prozessen die bereits laufen ist deswegen wichtig, da es ja sein könnte, dass das Script beim vorherigen Aufruf zwar ein pid-File angelegt hat, aber danach abgebrochen wurde (z.B. manuell durch den Benutzer) und das pid-File dadurch nicht gelöscht wurde.
Ist die Überprüfung auf eine laufende Instanz negativ, muss zuerst das pid-File angelegt werden (Die Variable $$ enthält die pid des aktuellen Prozesses).
Nach Beendigung der Arbeit sollte danach das pid-File wieder gelöscht werden um einen sauberen Abschluss zu bilden.
Das Script als Download gibts hier.
Ähnliche Artikel:
Dateien zählen unter Linux
Otto Normal hat damit unter Windows sicher keine Probleme und die meisten Linuxuser unter dem Betriebssystem ihrer Wahl wohl auch nicht. Will aber Otto mal in Linux reinschnuppern und dort Dateien zählen, stellt ihn das als Laien vor eine respektable Herausforderung.
Um sich den Inhalt eines Verzeichnisses aufzurufen ist ihm wahrschienlich schon der Befehl
ls
bekannt. ls steht für “list” Doch liefert dieser Befehl lediglich eine Auflistung des Inhalts des gegenwärtigen Verzeichnisses. Daher muss diese Ausgabe noch ausgewertet werden. Hierfür setzen wir Pipes | ein mit denen man mehrere Kommandos miteinander verbinden kann. Hierbei wird jeweils die Ausgabe des vorhergehenden Befehls als Eingabe für den nächsten Befehl herangezogen.
ls –1 | wc –l
wir haben hierbei den ls Befehl um den Parameter –1 (Zahl Eins) erweitert der für eine lange Listenform steht. Es wird somit jedes Element in einer extra Zeile angeführt. Die Ausgabe davon wird per Pipe an den wc Befehl weitergeleitet. wc steht in diesem Fall für Word Count. Erneut über einen Parameter –l (kleines L) weisen wir wc darauf hin, dass es die Zeilen zählen soll, die es übergeben bekommt.
Achtung! Wurde statt der 1 ein kleines L als Parameter an ls übergeben ist die Ausgabe um eins größer als die Anzahl der Elemente. Dies liegt daran, dass dabei noch eine weitere Zeile Output erzeugt wird, die keine Datei wiederspiegelt.
ls –l <targetdir> | wc –l
Wollen wir nicht direkt das Ergebnis aus dem Verzeichnis in dem wir uns befinden, können wir das Zielverzeichnis am ls Befehl anhängen.
Fürs erste kann man damit zufrieden sein, doch bei genauerer Betrachtung stößt man dabei rasch an die Grenzen des Befehls. Man zählt nämlich symbolische Links und Verzeichnisse und Dateien gleichermaßen.
Wir tauschen nun also den list Befehl gegen einen spezialisierteren Befehl aus: find
find <targetdir> –maxdepth 1 –type f | wc –l
Mit -type f schränkt man die Auflistung der Ergebnisse auf Datein (files) ein. Dies könnte man z.B. auch auf Verzeichnisse (directorys) einschränken: –type d
Achtung, bei der Auflistung der Verzeichnisse wird das gegenwärtige Verzeichnis . mitgezählt, vom Gesamtergebnis also wieder eins abziehen.
Von Haus aus würde der find Befehl alle Unterverzeichnisse absuchen. Mittels –maxdepth <zahl> kann die Tiefe die er dabei erreicht steuern. Die 1 aus dem Beispiel oben steht hierbei nur für das Wurzelverzeichnis. Möchte man lediglich eine Verzeichnisebene tiefer wäre –maxdepth 2 der passende Parameter.
Will man nun auch wieder die symbolischen Links in seinem Suchergebnis dabei haben muss man den find Befehl noch um den –follow Parameter erweitern
find <targetdir> –maxdepth 1 –type f –follow | wc –l
Möchte man nun noch auf Dateiendungen oder anderen Teilen des Dateinamens einschränken gilt es noch den -name '*.txt' Parameter zu erwähnen. Zwischen den Anführungszeichen wir der Suchbegriff angeführt, * und ? gelten dabei als die üblichen Wildcards.
find <targetdir> -maxdepth 1 -type f -follow -name '*.t?t' | wc -l
Happy counting!
PS: Rekursives suchen wäre mit dem ls Befehl mittels –R Parameter möglich
Ähnliche Artikel:
mail.log – versendete Mails erfassen

Bei der Kontrolle der Aktivitäten eines Mailservers bleibt einem oft nichts anderes über als sich durch die Logfiles zu wälzen. um einen Schnellen blick auf alle Email zu erhalten, die der Server zuletzt verschickt hat, kann man die mail.log Datei folgendermaßen filtern:
cat /var/log/mail.log | grep postfix | grep "to=<" | grep "status=sent"
Damit erhält Informationen zu allen versandten E-Mails: hier eine Beispielausgabe:
Mar 7 16:19:57 mailserver postfix/pipe[17596]: 753C62F9D: to=john@example.org, orig_to=<john@example.org>, relay=dovecot, delay=0.54, delays=0.01/0/0/0.53, dsn=2.0.0, status=sent (delivered via dovecot service)
Anhand der ID (In diesem Fall 753C62F9D lassen sich dann noch detailiertere Informationen zu den jeweiligen Emails auslesen, wie zB den Sender usw.
Für eine schnelle händische Kontrolle zwischendurch absolut ausreichend.
Wer schönere Ausgaben haben möchte splittet die Ausgabe noch etwas auf und lässt sich nur teile ausgeben:
cat /var/log/mail.log | grep postfix | grep "to=<" | grep "status=sent" | cut -d':' -f4-5 | cut -d',' –f1
Ergebnis:
753C62F9D: to=<john@example.org>
Bessere Logauswertungen und Zusammenfassungen bekommt man mit Tools wie zB pflogsumm, awstats oder mailgraph die in den Paket-Repositories der Distribution eurer Wahl auch vorhanden sein sollten.
Mit täglich gemailten Statistiken über pflogsumm habt ihr euren Server im Auge falls Unregelmäßigkeiten auftreten, Wochen- Monats und später mal Jahresstatistiken stellen euch die graphischen Tools awstats oder mailgraph zur Verfügung.
Bei soviel praktischer Hilfe ist das wachsame Auge über dem Mailserver ein leichtes :)
Ähnliche Artikel:
Maildir: Wie kommen die Mails auf einen neuen Server?
Es kommt der Zeitpunkt für viele Server, da werden sie ersetzt. So ergeht es auch so manchem Mailserver. Modernisierungen, Standortwechsel und andere Begebenheiten machen einen Transfer der alten Mails auf ein anderes Gerät / auf eine andere Installation notwendig.
Nun stellt sich die Frage, wie man das bewerkstelligt.
Die einzelnen Dateien im Maildir Format haben durchaus verzwickte Dateinamen
1992034886.M538544P12919.server,S=1473,W=1505
und so könnte man auf den trügerischen Schluss kommen, dass mehr da dahinter steckt und man irgendwelche Konvertierungen beachten muss. Doch es ist kein Transfer über 2 IMAP konten oder ähnliches ist notwendig. Der Dateiname ist lediglich ein Resultat des Versuchs eindeutige Dateinamen zu erzeugen und der Inhalt selbst ist simpler ASCII Text. Theoretisch könnte eine solche Datei auch “herbert” heißen und es sollten keinerlei Komplikationen auftreten.
Somit reicht es aus eine Datei im Maildir Format einfach in das neue Zielverzeichnis zu verschieben.
Was steckt hinter dem Dateinamen?
Als Prozedur für die Erzeugung des Dateinamens sind folgende zwei Formen vorgesehen:
- time.MusecPpid.host
- time.MusecPpid_unique.host
time und usec stehen für die aktuelle Systemzeit die über den aufruf gettimeofday ausgelesen wird, pid ist die Prozessnummer des Prozesses der die Mail in das Verzeichnis schiebt. Der Platzhalter host steht für den Namen der Maschine an die die Mail geliefert wurde.
Wenn mehr als eine Nachricht im selben Prozess behandelt wurde sind noch zusätzliche Suffixe angehängt, dies wird im 2. Schema verdeutlicht. Ein Unterstrich trennt die Prozessnummer von einem aufsteigenden Counter (unique). Dabei ist es egal in welchen Ordner eine Nachricht einmal abgelegt wird. Diese Mechanismen sorgen dafür, dass mehr als nur ein Prozess Mails ablegen kann ohne anderen Prozessen in die Wege zu kommen.
Damit der so erzeugte Dateiname nicht trotzdem schon existiert wird wird dennoch drauf geprüft ob er schon existiert. Wenn das der Fall wäre würde der Prozess für kurze Zeit pausieren und dann einen neuen Anlauf starten.
War der check erfolgreich wird die Nachricht angelegt. Ist sie fertig ins Filesystem geschrieben wird sie an ihr Ziel verschoben und um zusätzliche Informationen im Namen ergänzt. Der neue Name entspricht nun folgendem Schema:
- time.MusecPpidVdevIino.host,S=cnt,W=vsize
- time.MusecPpidVdevIino_unique.host,S=cnt,W=vsize
dev steht nun für die Device Nummer ino für die Inode Nummer, beides ausgelesen über fstat und cnt steht für die Nachrichtengröße in Bytes, welches für diverse Mailkonten-Quotenregelungen notwendig ist. Dadurch erspart man sich ein performance-lastiges Aufsummieren der Mailboxauslastung über den stat Befehl. vsize ist ebenfalls eine Kennzahl für die Mailgröße nach RFC822 bei dem die Steuerzeichen für den Zeilenumbruch mit berücksichtigt werden.
That’s it!
PS: richtiges chown nicht vergessen, sonst passiert nach dem Verschieben gar nix :)
Ähnliche Artikel:
Alle User mit UserID unter Linux auflisten
Aus gegebenen Anlass wollte ich einem User eine fixe User ID zuweisen. Vorab galt es zu verifizieren ob es bereits einen User mit entsprechender ID gibt.
Nun könnte man entweder per
id -u <benutzername>
einzeln nachprüfen, oder aber man wirft einen Blick in /etc/passwd. Dort wird man erstmals von einer einer Fülle an Information erschlagen. Freilich reicht der Blick da rein, doch was tun wenn nicht nur ~30 Benutzer am System vorhanden sind sondern 50+ oder gar 100+?
Auch hier gäbe es einen schnellen Weg:
cat /etc/passwd | grep <idnummer>
Aber mal ehrlich, auch wenns flott geht, wir wollen das noch dazu schön aufbereitet!
Hier kommt awk zum Zuge:
awk -F":" '{ print "Die UserID " $3 "\tist vom User: " $1 "\tbelegt"}' /etc/passwd | grep <idnummer>
Kurz erklärt, wir erzeugen uns eine eigene Ausgabe, und teilen den Inhalt von /etc/passwd in verschiedene Variablen auf. Dazu benutzen wir den Doppelpunkt als Trennzeichen: -F":"
'{ print"<inhalt>"}' damit weisen wir unsere individuelle Ausgabe an
$1 $2 $3 sind die jeweiligen Variablen die durch das aufteilen der Zeilen durch das Trennzeichen erzeugt wurden, auf Platz 1 steht der Username, auf Platz 3 die Userid.
Als letzte Variable geben wir noch das auszuwertende File an /etc/passwd.
Natürlich filtern wir wieder mittels | grep <idnummer> nach der von uns gesuchten ID und bekommen, bei einem Treffer nun eine kleine nette Ausgabe:
Die UserID 2222 ist vom User: awktest belegt




