Das BICsuite Run Program Teil 1: Einführung und einfache Anwendung

BICsuite und schedulix bieten leistungsfähige Funktionen, die es ermöglichen, Skripte im Scheduling Server zu speichern und auszuführen. Der Dreh- und Angelpunkt ist Run Program, das wir Ihnen in dieser Artikelreihe genauer vorstellen möchten. Die folgenden Ausführungen gelten auch für Rerun Program und Kill Program.

In der grafischen Benutzeroberfläche enthält das Eingabefeld Run Program die Definition der auszuführenden Kommandozeile. Der Inhalt dieses Feldes wird vom Scheduling-Server geparst und in eine Reihe von Argumenten aufgeteilt. Diese Argumente-Reihe wird verwendet, um den execv()-Systemaufruf oder einen anderen Systemaufruf derselben Familie, insbesondere execvp() aufzurufen. Um zu visualisieren, was unter der Haube passiert, verwenden wir ein kleines Programm, das die Anzahl der Argumente und deren Werte auf stdout auszugeben:

#include <stdlib.h>
#include <stdio.h>
int main (int argc, char *argv[]) 
{
    printf ("Number of arguments: %d\n", argc);
    for (int i = 0; i < argc ; ++i) {
        printf ("arg [%d] = -> %s <-\n", i, argv [i]);
    }
    return 0;
}

Der Name des Programms lautet printargs und wenn es (aus der Shell) aufgerufen wird, verhält es sich wie erwartet:

ronald@oncilla:~/$ ./printargs a b c d e
Number of arguments: 6
arg[0] = -> ./printargs <-
arg[1] = -> a <-
arg[2] = -> b <-
arg[3] = -> c <-
arg[4] = -> d <-
arg[5] = -> e <-

Die Werte der Argumente werden in -> und <- eingeschlossen, um das Quoting anzuzeigen, der nach dem Parsen des ausgeführten Programms durch den Scheduling-Server weitergegeben wird. Wenn der Programmaufruf in der GUI erfolgt, kümmert sich das System um die äußere Quoting-Ebene.

Beim Schreiben eines Befehls in einem Skript oder ähnlichem, der an den Scheduling-Server gesendet werden kann, muss sich der Benutzer selbst um das korrekte Quoting kümmern. Sofern nicht anders angegeben, lassen wir die äußere Quoting-Ebene weg und verhalten uns so, als ob die Werte des auszuführenden Programms im GUI eingegeben würden.

Einfache Anwendung

Zunächst wird das Dienstprogramm printargs mit einem einfachen festen Parameter aufgerufen. Im Grunde sieht das so aus:

run program = printargs hello world

Wie in der Einleitung erwähnt, haben wir die äußere Ebene des Quotings weggelassen. Wenn wir mit diesem Aufruf in Run Program eine Job-Definition erstellen und ausführen, ergibt das dieses Ergebnis:

Number of arguments: 3
arg[0] = -> printargs <-
arg[1] = -> hello <-
arg[2] = -> world <-

Interessanter wird es, wenn wir anfangen, Parameter zu verwenden. Diese Parameter können angesprochen werden, indem dem Namen des Parameters ein Dollarzeichen ($) vorangestellt wird. Ein Beispiel sieht so aus:

run program = printargs $JOBID

Hier wird der in der BICsuite vordefinierte Parameter JOBID verwendet. Der Wert entspricht der ID des Jobs:

Number of arguments: 2 
arg[0] = -> printargs <- 
arg[1] = -> 2305268 <-

Die vordefinierten Standardparameter sind für unsere Zwecke nicht besonders interessant. Wir verwenden deshalb einen Parameter namens TESTPARM, den wir in der Job-Definition definiert haben. Der Parametertyp ist Parameter, das gibt uns die Möglichkeit, seinen Wert zum Zeitpunkt der Submits anzugeben. (Da kein Standardwert angegeben wurde, müssen wir zum Zeitpunkt der Übermittlung einen Wert für TESTPARM angeben). Die Eingabe in Run Program sieht jetzt folgendermaßen aus:

run program = printargs $TESTPARM

Wenn wir TESTPARM so spezifizieren:

TESTPARM = Hello World

und den Job submitten, ergibt das dieses Resultat:

Number of arguments: 3
arg[0] = -> printargs <-
arg[1] = -> Hello <-
arg[2] = -> World <-

Wenn wir den Parameter TESTPARM als einzelnes Argument behandeln wollen, müssen wir ihn im Run Program in Anführungsstriche setzen:

run program = printargs "$TESTPARM"

Als Ergebnis bekommen wir dann:

Number of arguments: 2 
arg[0] = -> printargs <- 
arg[1] = -> Hello World <-

So weit so gut, aber eigentlich möchten wir ja diese Ausgabe erhalten:

Number of arguments: 2 
arg[0] = -> printargs <- 
arg[1] = -> $TESTPARM = Hello World <-

Die erste Herausforderung ist hier, das System dazu zu bringen, das Dollar-Zeichen $ nicht zu interpretieren. Es gibt zwei Wege, das zu erreichen. Welcher Weg der bessere ist, ergibt sich aus der Situation. Aber versuchen wir es zuerst mit einem Weg, der nicht funktioniert. Wir setzen beim Submit:

TESTPARM = $TESTPARM = Hello World

und versuchen die Job Definition damit zu submitten. Das führt zu einem Problem, denn der Scheduling Server reagiert mit einer Fehlermeldung:

Run into a loop while trying to resolve variable TESTPARM

Was ist hier geschehen? Der Scheduling Server löst Parameter rekursiv auf. Wenn er innerhalb eines Parameters auf einen Substring stößt, der durch das Dollar-Zeichen gefolgt von einer Zeichenfolge wie ein gültigen Identifier wirkt, versucht er diesen als Parameter aufzulösen. Weil aber der Identifier bereits den Parameternamen darstellt, gerät er in eine Endlosschleife. (Das System braucht den Wert von TESTPARM, um den Wert von TESTPARM zu ermitteln).
Offenbar müssen wir dem System mitteilen, dass $TESTPARM innerhalb des Parameters nicht aufgelöst werden soll. Das geschieht, indem das Dollar-Zeichen mit einem Backslash maskiert wird. Die Spezifikation sieht also nun so aus:

TESTPARM = \$TESTPARM = Hello World

Jetzt kann die Job Definition ausgeführt werden und liefert das gewünschte Ergebnis:

Number of arguments: 2 
arg[0] = -> printargs <- 
arg[1] = -> $TESTPARM = Hello World <-

Das Dollarzeichen (und der folgende Identifier) werden nicht entfernt und durch einen Parameterwert ersetzt. Stattdessen bleibt es an Ort und Stelle und das System entfernt nur den Backslash.

Wenn wir aber versuchen, diese Technik in Run Program anzuwenden, etwa so:

run program = printargs \$TESTPARM = $TESTPARM

Erleben wir die nächste Überraschung:

Number of arguments: 5 
arg[0] = -> printargs <- 
arg[1] = -> $TESTPARM <- 
arg[2] = -> = <- 
arg[3] = -> Hello <-
arg[4] = -> World <-

Plötzlich haben wir 5 Argumente anstelle von zweien. Dennoch scheint die Grundidee, das Dollarzeichen zu umgehen, zu funktionieren.

In der Shell (sh, bash, ksh, …) werden doppelte Anführungszeichen verwendet, um Zeichenketten zu verbinden, die durch Leerzeichen getrennt sind. Einfache Anführungszeichen können ebenso verwendet werden mit dem Unterschied, dass Variablen nicht aufgelöst werden. Ein Ausdruck wie etwa $PID wird als String behandelt, bei dem nach einem Dollar-Zeichen die Zeichen PID folgen und er wird nicht durch eine Process-ID ersetzt.

Versuchen wir es und schauen, was passiert. Wir beginnen damit, den Parameter in doppelte Anführungszeichen zu setzen:

run program = printargs "\$TESTPARM = $TESTPARM"

Und tatsächlich bringt das genau das Ergebnis, das wir uns wünschen:

Number of arguments: 2
arg[0] = -> printargs <-
arg[1] = -> $TESTPARM = Hello World <-

Es sieht so aus, dass sich das Verhalten des Scheduling Servers und das der Bourne-Shell ähneln. Wenn wir versuchen, das zu prüfen, indem wir anstelle der doppelten einfache Anführungszeichen verwenden, erhalten wir:

Number of arguments: 2
arg[0] = -> printargs <-
arg[1] = -> \$TESTPARM = $TESTPARM <-

Das sieht gut aus und scheint genau das zu sein, was zu erwarten war. Aber tatsächlich interpretiert das System durch die einfachen Anführungszeichen einen Backslash einfach nur als Zeichen und nicht als Escape Zeichen, das die Bedeutung des darauf folgenden Zeichens ändert. Das liegt daran, dass das Dollar-Zeichen innerhalb von einfachen Anführungszeichen keine besondere Bedeutung mehr hat.

Die eindeutige Spezifikation für den Aufruf in Run Program:

run program = printargs ’\\$TESTPARM = $TESTPARM’

liefert das selbe Ergebnis wie zuvor. In diesem Fall definiert der erste Backslash, dass der nächste Backslash wie ein normales Zeichen behandelt werden soll. Dieses Verhalten spielt eine Rolle, wenn das Zeichen hinter dem Backslash eine besondere Bedeutung hat, wie es zum Beispiel bei einem einfachen Anführungszeichen der Fall ist.
Eine Spezifikation wie diese:

run program = printargs ’\’

wird nicht funktionieren, weil das zweite einfache Anführungszeichen durch den Backslash ausgehebelt wird. Je nachdem, was gewünscht ist, lautet der Aufruf entweder:

run program = printargs ’\\’

was einem Backslash als Argument von printargs entspricht, oder:

run program = printargs ’\’’

was einem einfachen Anführungszeichen als Argument von printargs entspricht.

Da wir herausgefunden haben, dass die Interpretation von Run Program vielen Regeln der Bourne-Shell folgt, können wir unser Ziel auch mit einer Kombination aus einfachen und doppelten Anführungszeichen erreichen:

run program = printargs ’$TESTPARM = ’"$TESTPARM"

Weil sich kein Leerzeichen zwischen dem zweiten einfachen und dem ersten doppelten Anführungszeichen befindet, wird das Argument an dieser Stelle nicht in zwei Argumente aufgeteilt. Und tatsächlich sieht das Ergebnis so aus, wenn wir printargs so aufrufen:

Number of arguments: 2
arg[0] = -> printargs <-
arg[1] = -> $TESTPARM = Hello World <-

Teil 1: Einführung und einfache Anwendung

Teil 2: Fortgeschrittene Anwendung

Teil 3: Backticks

Teil 4: Andere Interpreter

Teil 5: Umgehung von Einschränkungen

Teil 6: Sicherheitsüberlegungen und Fazit