nullpointer.at

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
#    https://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:


Beitrag veröffentlicht

in

, , ,

von

Kommentare

5 Antworten zu „Mit Bash prüfen ob das eigene Script gerade läuft“

  1. Avatar von Markus Winand

    Sieh dir doch auch mal meine HA-Tools an: http://fatalmind.com/software/hatools/

    Mit halockrun kann man das auch machen. Ich behaupt mal: zuverlässiger. hatimerun dabei auch sehr nützlich sein, damit das zeug ggf. nicht ewig hängt.

    -markus

  2. Avatar von Planzo

    Danke für das tolle Skript.
    Allerdings funktioniert es mit deiner Zeile 27 „ps -e | grep…“ bei mir nicht, da die Namen der Prozesse nach 8 Buchstaben abgeschnitten wurden. Habe das „ps -e“ durch „ps xa“ ersetzt und nun funktioniert es tadellos.

  3. Avatar von Jan W.
    Jan W.

    Ich benutze erfolgreich folgenden Code-Schnipsel, um die dopplete Ausführung eines Skripts zu unterbinden:

    #!/bin/bash
    # check, if script is already running
    LOCKFILENAME=“/var/lock/`basename $0`“
    if [ -z „$FLOCK_SET“ ] ; then
    exec env FLOCK_SET=1 flock -n „${LOCKFILENAME}“ „$0“ „$@“
    fi
    # do some useful suff here
    # …

  4. Avatar von Fubar
    Fubar

    [code]
    SCRIPTNAME=“$(/usr/bin/basename „$0″)“
    LOCKFILE=“/run/lock/$SCRIPTNAME-$USER.lock“

    function lock_or_exit () {
    local lock=“$LOCKFILE“
    if /usr/bin/pgrep –pidfile „$lock“ >/dev/null 2>&1; then
    echo „E: Es läuft bereits eine Instanz (PID=$(cat „$lock“))“ >&2
    exit
    else
    echo $$ > „$lock“ || exit # Schreibe PID in d. Sperrdatei
    trap „rm -f ‚$lock’“ 0 1 2 3 15 # Lösche Sperrdatei beim Beenden, Abbruch …
    fi
    }

    lock_or_exit
    [/code]

  5. Avatar von BASHER
    BASHER

    Das ist doch viel zu kompliziert… es geht mit der aktuellen BASH wesentlich einfacher:

    #!/bin/bash # Shebang

    INSTANZ() { echo „Abbruch: laufende Instanz gefunden!“
    exit 1
    }

    PID=${$} # Prozess-ID dieses aktuellen Skriptes
    PFAD=${0%/*} # Pfad zum Skript aus ${0} separieren
    PROG=${0##*/} # Skriptname aus ${0} separieren
    DPID=$(pgreb -o ${PROG} | grep -v ${PID}) # ältere laufende Instanz detektieren
    [[ ${DPID} != „“ ]] && INSTANZ # Skript beenden (andere Instanz entdeckt)

    … hier das Skript fortsetzen …

    Im Endeffekt benötige ich sogar nur 3 Zeilen dafür und es ist wesentlich verständlicher.
    PiD=${$} lädt die aktuelle Prozess-ID dieses Skriptes in die Variable ${PID}.
    PFAD=${0%/*} separiert aus der Variable ${0} den Pfad zum Skript.
    PROG=${0##*/} separiert nur den Skriptnamen aus der Variable ${0}.
    Ich hole mit pgrep alle PIDs mit meinem Skriptnamen in die Variable ${DPID}, sortiere meine PID mit grep -v aus, so das in ${DPID} bestenfalls keine andere PID drin steht oder aber eine oder mehrere andere PIDs weiterer Instanzen meines Skriptes. Danach prüfe ich mit TEST, ob in der Variable ${DPID} etwas drin steht oder nix „“, dann weiss ich, ob eine weitere Instanz vorhanden ist, woraufhin ich mit && die Funktion „INSTANZ“ aufrufe (hier könnte auch knallhart direkt ein „exit 88“ oder so stehen, welches ein sofortiges Ende bewirkt. pgrep ist ein toller Zugewinn und arbeitet wesentlich besser als ps.

    Mit freundlichen Grüssen
    Der BASHER

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

*