Benutzer-Werkzeuge

Webseiten-Werkzeuge


users:werner:multcmd

Mehrere Befehle ausführen lassen

Das Problem, das ich hier zu lösen versuche, ist das Problem der 20 Hände ;-) Ich muss auf einer großen Anzahl von VMs (Linux-Systemen) immer wieder dieselben oder zumindest sehr ähnliche Befehle ausführen. Das erfordert das Anmelden auf den ca. 20 VMs, das Eintippen der Befehle (mit den nicht ausbleibenden Fipptehlern) usw. Lässt sich das vereinfachen? Bestimmt, aber mit welchem Aufwand?

Grundsätzlich lassen sich alle Befehle scripten. Ich kann also für jede auszuführende Aktion ein Script verfassen und muss es dann auf dem Host ausführen lassen. OK. Und wie?

Weil meine Hosts alle auf ein gemeinsamens Filesystem zugreifen können1), kann ich so tun, als wäre das alles lokal. Das vereinfacht die Sache wesentlich. Nun muss ich nur noch ein Script schreiben, das nach Art eines Schedulers alle X Minuten2) nachsieht, ob etwas Neues abzuarbeiten ist.

Vom Aufbau her recht einfach:

  • nachsehen, ob ein Anweisungsfile existiert
  • wenn es da ist, einlesen (klar ;-))
  • in der Datei steht eine ID, die mit jeder neuen Dateiversion ansteigt
  • in der Datei steht ein Array mit abzuarbeitenden Befehlen
  • wenn die ID der Datei größer ist als die zuletzt bearbeitete ID: führe die Befehle aus
  • schreibe die ID in eine Merkdatei für den nächsten Aufruf

Umgesetzt habe ich das mit Hilfe der Bash. Das Script sollte ab Bash Version 4 laufen3). Eine kleine Besonderheit ist noch drin: das Umkopieren des übergebenen Arrays CMDARR nach COMDAR. Wenn nämlich in der Befehlsdatei ein Index fehlen sollte, werden die Arrayelemente ordentlich nummeriert in den neuen Array geschrieben. Die Anzahl der Elemente bleibt, aber hinterher klappt auch die Schleife über die Arrayelemente ;-)

Und wer wissen will, warum ich hier nicht das gelegentlich beschriebene for COMD in ${CMDARR[*]} (oder${CMDARR[@]}) benutze, mag es bitte mal selbst ausprobieren ;-). Es ergibt sich dabei nämlich, dass jedes einzelne Wort von der FOR-Schleife als separates Element genommen wird (also jeder einzelne Befehl an den Leerzeichen aufgetrennt wird) *mööp*, setzen, 6 :-(

das Arbeitsscript

workcmds.sh
#!/bin/bash
#######################################
#
#  purpose of this script is to execute
#+ various commands that are transmitted
#+ in a separate file "$(basename $0 .sh).inc"
#+ in the given order.
#+ The number of commands will vary,
#+ as will their nature ;-)
#
#  written by w.flamme@web.de 2011-09-06
#+ with help from http://tldp.org/LDP/abs/html/
 
# will be needed ;-)
PATH=/bin:/usr/bin:/sbin:/usr/sbin
umask 002
 
# the array to hold the commands
declare -a CMDARR
 
# the file that will contain the array
CONFILE="$(basename $0 .sh).inc"
# the file that will contain the last executed command id
LASTFILE="$(basename $0 .sh).last"
 
# the command file has an id to identify the sequence
LASTID=$(< "$LASTFILE")
# this /must/ have a value...
LASTID=${LASTID:-0}
 
# if the file with the commands exists...
if [ -f "$CONFILE" ]; then
    # ...read it...
    . "$CONFILE"
    # if CMDID is not set, assign a default value
    CMDID=${CMDID:-0}
    # if the read CMDID hasn't been processed yet...
    if [ $CMDID -gt $LASTID ]; then
        # how many commands do we have?
        NUMCMDS=${#CMDARR[@]}
        # if we have any, ...
        if [ $NUMCMDS -gt 0 ]; then
            # do someting with them
            COMDAR=(${CMDARR[*]})
            for (( I = 0 ; I < $NUMCMDS ; I++ )) ; do
                # namely: execute them :-)
                "${!COMDAR[$I]}"
            done # for (( I = 0 ; I < $NUMCMDS ; I++ )
        # note CMDID for later use
        echo $CMDID > "$LASTFILE"
        fi # if [ $NUMCMDS -gt 0 ]
    fi # [ $CMDID -gt $LASTID ]
fi # if [ -f "$CONFILE" ]

Beispielbefehle

Die eingelesene Datei sieht dann etwa so aus:

workcmds.inc
#!/bin/bash
 
CMDID=3
 
CMDARR[0]="echo 'erster Befehl'"
CMDARR[1]='echo ...und weiter...'
CMDARR[2]="echo PATH=$PATH"
CMDARR[3]="echo 'letzter Befehl'"

Sieht beherrschbar aus – mal sehen, wie es sich bewährt :-)

Abwandlungen fallen mir schon ein:

  • man schaut nicht nach einer Datei, sondern arbeitet alle Dateien in einem bestimmten Verzeichnis ab. Dazu müssten dann die Befehls-IDs in der Datei durch die Dateinamen ersetzt werden (eindeutig, steigend). Dann könnte man allerdings auch fertige Scripte direkt in das Verzeichnis einstellen…
  • die erledigten Dateien des Verzeichnis' werden in einer Datei (deren Name den jeweiligen VM-Hostnamen enthält) gespeichert. Dadurch müssen die Dateinamen zwar noch eindeutig sein, aber nicht mehr aufsteigend.
1)
aber man könnte hier auch eine Datei z. B. mit wget herunterladen
2)
das regelt cron
3)
auch mit früheren Versionen, wenn man LASTID=$(< "$LASTFILE") durch LASTID=$(cat "$LASTFILE") ersetzt
users/werner/multcmd.txt · Zuletzt geändert: 2011-09-10 20:15 von werner

Falls nicht anders bezeichnet, ist der Inhalt dieses Wikis unter der folgenden Lizenz veröffentlicht: Public Domain
Public Domain Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki