MultiTOS für Einsteiger Teil 7 - MultiTOS goes UNIX Teil III

Ich gebe es zu, irgendwie ist mein Verhältnis zu Benutzeroberflächen gespalten (ACHTUNG! Outing). Einerseits bin ich ein Mausfanatiker und arbeite, selbst wenn Tastaturbefehle vorhanden sind, lieber mit dem Nager. Andererseits verehre ich UNIX im allgemeinen, und im speziellen die Korn-Shell. Eventuell können Sie meine Gespaltenheit nach diesem Kursteil ein wenig nachvollziehen.

Was z.B. eine Korn-Shell unter UNIX bzw. MiNT mit geringem Aufwand so alles möglich macht, muß einfach begeistern. Okay, der Einstieg ist dank einer Menge kryptisch anmutender Befehle nicht ganz so einfach wie die Bedienung eines Desktops mit der Maus, aber für viele kleine (und auch große) Probleme im Computeralltag finden sich mit Hilfe einer (Korn-) Shell schnelle Lösungen ...

Die Theorie

Wie in jeder anständigen Programmiersprache, stehen natürlich auch für die Script-Programmierung unter der Korn-Shell Konstrukte zur Ablaufsteuerung zur Verfügung. Im folgenden Text steht „BEFEHLE_" für eine praktisch beliebige Kommandofolge, also gerne auch mehrere Kommandos in mehreren Zeilen! Wenn mehrere Befehle Verwendung finden, zählt der Rückgabewert des letzten.

Zuerst die allseits bekannte bedingte Ausführung namens if:

if BEFEHLE_1
    then BEFEHLE_2
fi

Hier eine Kommandoschleife mit for in der ersten Version:

for VARIABLE_1 
    do BEFEHLE_1 
    done

Und nun die Kommandoschleife mit for in einer weiteren Version:

for VARIABLE_1 in BEZEICHNER_1 
    do BEFEHLE_1 
    done

Das ist nochmals eine Kommandoschleife, diesmal mit while:

while BEFEHLE_1 
    do
        BEFEHLE_2
    done

Und als letzte Kommandoschleife eine Version mit until:

until BEFEHLE_1 
    do
        BEFEHLE_2
    done

Auch Sprungkaskaden mit case sind vorhanden:

case BEZEICHHER_1 in BEZ_2)
    BEFEHLE_1 ;; BEZ_3) BEFEHLE_2;;
    ...........
    esac

Sprünge ans Ende einer Kommandoschleife macht continue möglich:

continue

Und auch das Beenden einer Schleife via break ist vorgesehen:

break

if then fi

Optional können auch noch else- bzw. elif-then-Zweige eingefügt werden, vorerst wollen wir uns aber mit der einfachen Version begnügen. Für BEFEHLE_1 hinter dem if können im Prinzip beliebige Befehle stehen, dit BEFEHLE_2 zwischen then und fi werden nur dann ausgeführt, wenn BEFEHLE_1 das Ergebnis 0 liefert! Das mag zwar auf den ersten Blick unsinnig erscheinen, da in herkömmlichen Programmiersprachen eine 0 eigentlich „unwahr“ bedeutet und damit eben der then-Zweig nicht ausgeführt würde, aber unter UNIX bedeutet die Rückgabe von 0, daß ein Programm ordnungsgemäß ausgeführt wurde. Es gilt also: Wenn BEFEHLE_1 erfolgreich ausgeführt wurde, wird BEFEHLE_2 ausgeführt, sonst wird nach dem fi. weitergearbeitet. Ein einfaches Beispiel mit einem else-Zweig:

if ls 
   then
    echo "Es hat funktioniert" 
   else
    echo "Das war wohl nichts"
fi

Wenn der Befehl ls erfolgreich war, wird „Es hat funktioniert“ ausgegeben, ansonsten „Das war wohl nichts“.

Damit der Nutzen von if usw. nicht nur auf die Abfrage externer Programme beschränkt bleibt, sondern auch Vergleiche usw. möglich sind, gab es in älteren Systemen den Befehl test, der ist inzwischen von den „eckigen Klammern“ [ ] abgelöst worden. Der Vergleich [$DJJMMY = = hallo] würde 0 und damit „wahr“ ergeben, wenn in der Variablen D UMMY der String „hallo“ gespeichert ist. Die wichtigsten vier Vergleiche bzw. Abfragen sind:

[ XXXX = YYYY ] gibt 0, wenn XXXX gleich YYYY ist

[ XXXX != YYYY ] gibt 0, wenn XXXX ungleich YYYY ist

[ -d XXXX ] gibt 0, wenn XXXX ein Verzeichnis ist

[ -f XXXX ] gibt 0, wenn XXXX eine Datei ist

Selbstmurmelnd gibt es noch viel mehr Vergleiche und Abfragen, eine ausführliche Zusammenfassung finden Sie in den Manual-Pages Ihrer Shell.

for in do done

Die einfache Form for do done füllt VARIABLE_1 nacheinander mit den Parametern, die dem Script übergeben wurden, diese Schleife wird also $# (die Anzahl der Parameter) mal ausgeführt. Ein Beispiel:

for p 
    do
        echo $p 
    done

Angenommen, wir speichern dieses Script mit dem Namen t1.ksh und mfen es mit t1 1 2 3 4 auf, dann würde auf dem Bildschirm

1
2
3
4

ausgegeben.

Die erweiterte Version for in do done füllt VARIABLE_1 mit dem Inhalt von BEZEICHNER_1. Am besten schauen wir uns auch das in einem Beispiel an:

for p in 1 2 3 4 
    do
        echo $p 
    done

Diesmal speichern wir folgendes Script als t2.ksh und rufen es einfach mit t2 auf, und schon wird auch hier

1
2
3
4

auf dem Bildschirm ausgegeben. Natürlich können für BEZEICHNER_1 praktisch beliebige Ausdrücke verwendet werden. Das Script

for p in * 
do
    echo $p 
done

zum Beispiel gibt alle Dateinamen im aktuellen Verzeichnis aus!

while do done

Solange das Ergebnis von BEFEHLE_1 0 ist, wird der Teil zwischen do und done (BEFEHLE_2) abgearbeitet. Im folgenden Beispiel wird solange die Uhrzeit ausgegeben, bis es genau 12 Uhr ist:

while [ $TIME != "12:00:00" ] 
do
    echo $TIME
done

until do done

Diese Schleife ist das Gegenstück zu while do done. Solange das Ergebnis von BEFEHLE_1 nicht 0 ist, wird BEFEHLE_2 abgearbeitet.

until [ $TIME = "12:00:00" ] 
do
    echo $TIME 
done

Nochmal die Uhrzeit, dieses „Untier“ liefert das gleiche Ergebnis wie das Konstrukt mit while do done. Man beachte allerdings das „=”-Zeichen!

case in esac

Diese Sprungkaskade ist besonders nützlich, da man mit ihrer Hilfe sehr einfach Unterscheidungen treffen kann. Auch hier ein Beispiel:

for i in * 
do
    case i in
        a*) echo $i beginnt m. einem a ;; 
        b*) echo $i beginnt m. einem b ;;
        *) echo $i beginnt nicht mit a oder b ;;
    esac
done

In der for-Schleife wird die Variable i nach und nach auf die Dateinamen im aktuellen Verzeichnis gesetzt, und in der case-Struktur wird entschieden, ob die Dateinamen mit einem a oder b beginnen. Wichtig ist hierbei, daß die einzelnen case-Fälle mit einem Ausdruck gefolgt von einer Klammer „)” beginnen und mit einem doppelten Strichpunkt „;;” enden.

Die Praxis

Soviel zur Theorie, nun aber in die Praxis. Wer eine größere Festplatte besitzt, wird sicher auch schon verzweifelt nach einem bestimmten Ordner gesucht haben, frei nach dem Motto: Ja wo is’ er denn? Mit Hilfe der Scripts makelist.ksh (Listing 1) und dirfind.ksh (Listing 2) wird das Leben leichter ... Eine Möglichkeit wäre es, bei jeder Suche die komplette Festplatte durchzuwühlen, aber das dauert viiiiel zu lange, also legen wir uns eine Liste mit allen Verzeichnissen an und suchen in dieser. Am besten, Sie tippen diese beiden Scripts ab und verfrachten sie in das Verzeichnis /usr/bin (Sie erinnern sich, unter MiNT sollte man mit der Shell immer auf dem Laufwerk U: arbeiten!). Wie funktioniert denn nun das ganze? Also nehmen wir uns zuerst das makelist.ksh vor: Dieses Script gibt alle Ordner auf allen Laufwerken (außer A: und B:) auf dem Bildschirm und einer Textdatei im Verzeichnis /etc aus. Als Lieferant für die Ordnernamen dient der Befehl ls. Mit dem Parameter -R1 wird er dazu überredet, rekursiv alle Ordner und Dateinamen ab dem aktuellen Verzeichnis auszugeben. Es lebe der kleine Unterschied! Als freundlicher Helfer gibt ls die Namen der Ordner, gefolgt von einem „;” aus! Den einfachen Dateien fehlt dieses Anhängsel. Dieses feine Unterscheidungsmerkmal machen wir uns zunutze und geben eben nur die Namen aus, die am Ende einen Doppelpunkt haben, und schon ist die Liste fertig. Zugegeben, es hört sich einfach an, aber zur Realisierung müssen wir noch zwei Neuheiten kennenlernen, zum einen den Befehl read, er liest von der Standardeingabe und speichert das Gelesene in Variablen; zum anderen die Möglichkeit, die Standardeingabe auch innerhalb eines Scripts umzulenken. Wer sich Listing 1 schon einmal näher angesehen hat, dem dürften besonders folgende Zeilen ins Auge gefallen sein:

ls -R1 | (
while read P1
do
    case $P1 in
        *:) echo /$i/$P1 >>/etc/dir_list 
        echo /$i/$P1
        ;;
    esac 
done )

Das Zeichen „|” leitet ja bekanntlich die Standardausgabe um, nur wohin? In diesem Falle landet sie bei dem read-Befehl nach der öffnenden Klammer! Das bedeutet, die Standardausgabe von ls wird mit der Standardeingabe der Befehle zwischen den Klammem verbunden. Der einzige Befehl, der hier von der Standardeingabe liest, ist read, und das war’s dann auch schon. Nützlich, nicht?

Damit hätten wir die Liste; aber was noch fehlt, ist das Durchsuchen. Keine Panik, dafür gibt es dirfind.ksh. Dieses Script verwendet den gleichen Trick. Mit dem Befehl cat wird die Datei /etc/dir_list, die wir vorher mit makelist.ksh erzeugt haben, auf die Standardausgabe ausgegeben. Eben diese Ausgabe lenken wir wieder in einen read-Befehl um und suchen via case nach dem Gewünschten.

Zum Schluß wenden wir uns noch dem Listing 3 zu. Hier findet sich ein erweitertes man-Script, das es ermöglicht, zu mehreren Befehlen gleichzeitig Hilfe anzufordern. Z.B. bringt der Aufruf man ls du df nacheinander die Manual-Seiten zu den Befehlen ls, du und df auf den Bildschirm. Hier werden noch einmal alle besprochenen Techniken verwendet, plus einer weiteren Neuerung, der Definition von Funktionen. Mit diesen Shell-Funktionen und einigen weiteren Befehlen der Shell werden wir uns im nächsten und letzten Kursteil beschäftigen. Noch eine Anregung für eigene Experimente: Shell-Funktionen sind eigentlich Scripts im Script, bei ihrem Aufruf werden eventuelle Parameter beinahe genauso behandelt, als ob es sich um den Aufruf eines andern Scripts handeln würde. Viel Spaß beim „Basteln“ ...


# Listing 1: makelist.ksh
# (c)1994 by MAXON-Computer
# Autor: Richard Kurz
# Script zum Anlegen einer Liste
# aller Verzeichnisse...

echo Die Verzeichnis-Liste wird erstellt!
echo Bitte warten...
echo

# Zur Wurzel 
cd /

# Die Datei wird angelegt bzw. gelöscht 
echo >/etc/dir_list

# Die Laufwerke in U:/ abklappern 
for i in ? 
do
    # Ist's auch wirklich ein Verzeichnis?
    if [ -d $i ]
    then
        # A: und B: werden ausgelassen 
        case $i in
            a)   ;;
            b)   ;;
            # rein in’s Laufwerk 
            *) cd $i
                # ls rekursiv ausgeben lassen und
                # die Ausgabe umleiten 
                ls -R1 | (
                # Hier landet die Ausgabe von ls
                # und wird in P1 eingelesen 
                while read P1
                do
                    # Nur die Zeilen mit einem : am
                    # Ende durchlassen 
                    case $P1 in
                        # Und ausgeben...
                        *:) echo /$i/$P1 »/etc/dir_list echo /$i/ $P1
                            ;;
                    esac 
                done )
                # Back to the roots 
                cd ..
                ;;
        esac
    fi
done

echo
echo fertig...

# Listing 2: dirfind.ksh
# (c)1993 by MAXON-Computer
# Autor; Richard Kurz
# Script zum Suchen von Verzeichnissen in einer
# Liste, die mit MAKELIST angelegt wurde

# Überprüfung, ob ein Parameter angegeben ist... 
if [ $# = 0 ]
then
    echo Fehler: Parameter fehlt! >&2 
    echo Beispiel: dirfind auto >&2 
    exit 1
fi

# Ausgeben der Einträge in /etc/dirlist via cat 
cat /etc/dir_list | (
while
    # Die Ausgabe von cat landet hier 
    read P1
do
    case $P1 in
        # Wenn der Suchstring enthalten ist,
        # dann ausgeben...
        *$1*) echo $P1 ;;
    esac 
done )

# Listing 3: MAN.KSH
# (c)1994 by MAXON-Computer
# Autor: Richard Kurz
# Einfacher man-Befehl für die KomSHell

# Funktion zum Anzeigen der gefundenen # Manual-Seiten. . .
show_it()
{
    case $2 in
        # Wenn sich der Text in einem
        # cat-Verzeichnis befindet, wird
        # er via more ausgegeben 
        cat*) more $1 ;;

        # In den man-Verzeichnissen finden
        # sich Texte mit Steuerinformationen
        # die via nroff und more ausgegeben werden 
        man*) nroff -man $1|more ;;

        # Im Zweifelsfalle nehmen wir more...
        *)      more $1 ;;
    esac

    # Wir habe was gefunden 
    FOUND=true
}

# Funktion zum Durchsuchen der Manual-
# Verzeichnisse...
search_it()
{
    # Alles im Manual-Verzeichnis durchsuchen 
    for i in *
    do
        # Aber latürnich nur Verzeichnisse 
        if [ -d $i ]
        then
            # Rein in's Verzeichnis 
            cd $i
            # Nach passenden Einträgen suchen 
            for j in $1.*
            do
                # Wenn's eine Datei ist,
                if [ -f $j ]
                then
                    # dann Ausgeben... 
                    show_it $j $i
                fi
            done
            # Und zurück 
            cd ..
        fi
    done
}

# Hier startet das Ganze...

# Überprüfung, ob ein Parameter angegeben # ist... 
if [ $# = 0 ]
then
    echo Fehler: Parameter fehlt! >&2
    echo Beispiel: man ls >&2
    exit 1
fi

# Ist das Manual-Verzeichnis ok? 
if [ -d $MANPATH ]
then
    # Jau, dann 'reingehen 
    cd $MANPATH
else
    echo Kein Manual-Verzeichnis vorhanden! 
    exit 1
fi

# Nun werden die Parameter abgearbeitet... 
for p
do
    # Variable einrichten 
    FOUND=false

    # Nach den Texten suchen 
    search_it $p

    # Hilfe gefunden oder nicht... 
    if [ $FOUND = false ]
    then
        echo
        echo Keine Manual-Seiten zu $p gefunden! >&2 
        echo Bitte die Taste '"Return"- drücken
        # Hier wird einfach auf ein Return gewartet,
        # die Variable TASTE ist eigentlich nur ein
        # Dummy 
        read TASTE
    fi
done

Richard Kurz
Aus: ST-Computer 04 / 1994, Seite 100

Links

Copyright-Bestimmungen: siehe Über diese Seite