von Achim Schmidt
Teil 3: Anwendungsbeispiel
Nachdem wir in den letzten beiden Teilen die Definition und Anwendung des CGI Interfaces behandelt haben, werde ich in diesem Teil ein komplexeres Anwendungsbeispiel erläutern. Als Beispielanwendung habe ich mir eine Online - Adressendatenbank gewählt, welche das anlegen, suchen, ändern und löschen von Datensätzen erlaubt. Das Programm ist eine CGI Anwendung, welche ohne jegliches Datenbankinterface zurande kommt. Vorab möchte ich noch erwähnen, daß es durchaus möglich ist, das Programm zu optimieren indem man mehrer Quelltexte zusammenfaßt und effizienter programmiert. Ich habe hiet jedoch zugunsten der besseren Erklärbarkeit und Übersichtlichkeit auf diese Optimierung ganz bewußt verzichtet.
Als erstes sollten wir uns einmal ueberlegen, welche Funktionen innerhalb des Programmes ermöglicht werden sollen. Ich habe mich dabei für folgenden Funktionsumfang entschieden:
Da kein Datenbankinterface benutzt wird, muss noch eine eindeutige Struktur für das Datenfile erstellt werden. Wir verwenden für unser Beispiel eine ASCII Datei mit dem ^ - Zeichen als Feldtrenner und einer Zeile pro Datensatz. Folgende Felder sollen beinhaltet werden:
Dementsprechend sieht der Schematische Aufbau jeder Zeile aus:
Name^Vorname^Strasse^Plz^Ort^Telefon^Fax^Email^Homepage^
Um das System etwas transparenter und felxibler zu machen, verwenden wir keinen festen Path zum Datenfile hin, sondern gehen einen indirekten Weg über die Datei ao-dppath.cfg, in welcher der komplette Path zum Datenfile steht. Damit ist es einfacher das Programm auf mehreren System zu installieren.
Die Suchfunktion soll dabei gleich auf der Startseite umgesetzt werden, da die meisten Nutzer wohl nach Adressen suchen moechten. Das Hauptmenue soll also folgendermaßen aussehen:
Dementsprechen sieht die Startseite des Programmes aus:
Listing 1: index.html (Hauptmenue):
<title>Adressen Online</title>
<h2><center>Achim's Adressbuch Online</center></h2>
<hr>
<center>
<form action=suchen.cgi method=get>
Suchbegriff: <input type=text name=suchname size=30>
Suchen im Feld: <select NAME=feld>
<OPTION value=1 selected>Nachname
<OPTION value=2>Vorname
<OPTION value=3>Strasse
<OPTION value=4>Plz
<OPTION value=5>Ort
<OPTION value=6>Telefonnummer
<OPTION value=7>Faxnummer
<OPTION value=8>Email
<OPTION value=9>Homepage
</SELECT>
<input type=submit value="Suche starten">
</form>
</center>
<hr>Administrative Funktionen:
<a href=ao-eintragen.html>Eintragen</a>
<a href=ao-db-sort.cgi>Datenbank sortieren</a>
<a href=ao-loeschen.html>Datensatz loeschen</a>
<a href=ao-aendern.html>Datensatz aendern</a>
<hr>
<address><font size=2>ADDRESS ONLINE (c) Achim Schmidt</font></address>
Wesentlich an diesem Programm wäre das Formular zur Suche (<form action=suchen.cgi method=get>). Hier erfolgt die Aufforderung zur Eingabe des Suchbergriffes und die Zuweisung des Suchbegriffes zu einem Datenbankfeld in einer Selectbox., welche einen Numerischen Code zurückliefert, der an das CGI Skript übergeben wird.
Die Suche selbst wird ueber das Skript suchen.cgi eingeleitet, welches die Benutzereingaben aus der Startseite entgegennimmt, aufbereitet und die Suche mittels eines awk-Skriptes durchführt (suchen.awk).
Listing 2: suchen.cgi :
#!/bin/sh
datenfile=`cat ao-dbpath.cfg`
SUCHNAME=`./cgiparse -value suchname`
FELD=`./cgiparse -value feld`
echo "Content-type: text/html"
echo
echo "<TITLE>ADRESS ONLINE</TITLE>"
echo "<CENTER><H3>Suchen nach: $SUCHNAME</H3></CENTER><HR><p>"
echo "<CENTER>"
echo "<TABLE BORDER>"
echo "<TD>NACHNAME<TD>VORNAME<TD>STRASSE<TD>PLZ<TD>ORT<TD>TELEFON<TD>FAX<TD>EMAIL<TD>HOMEPAGE<TD><TR>"
awk -f suchen.awk FELD=$FELD SUCHNAME=$SUCHNAME $datenfile
echo "</TABLE>"
echo "</CENTER>"
echo "<p>"
echo "<HR>"
echo "<A HREF=./>HAUPTMENUE</A>"
echo "<HR><FONT SIZE=2>"
echo "<ADDRESS>ADDRESS ONLINE (c) Achim Schmidt</ADDRESS></FONT>"
In der ersten Zeile folgt die zuordung der ausführenden Shell. In der zweiten Zeile wird dann der Path zum Datenfile aus der Konfigurationsdatei herausgelesen.
Die folgendenbeiden Zeilen legen die Übergabeparameter in Shellvariablen ab. Dabei enthällt die Variable SUCHNAME den Suchbegriff und die Variable FELD die numerische Bezeichnung des Feldes in welchem der Begriff zu suchen ist.
In den folgenden Zeilen wird dem System der Ausgabetyp html mitgeteilt und die Kopfzeile der Ausgabetabelle festgelegt.
Die Recherche selbst wird in der Zeile
awk -f suchen.awk FELD=$FELD SUCHNAME=$SUCHNAME $datenfile
mittels des awk - Skriptes suchen.awk gestartet. Dabei wird dem AWK Skript sowohl die Suchbezeichnung (SUCHNAME=$SUCHNAME) als auch die Feldnummer (FELD=$FELD) als Parameter übergeben. Am Ende der AWK Zeile, steht der Path der Datendatei, welcher vom Shellskript in die Variable $datenfile gelegt wurde.
Anschließend wird noch das Tabellen und Seitenende der Ausgabeseite geschrieben, welches wiederum einen Link auf das Hauptmenue enthält.
Die wesentlichste Aufgabe bei der Recherche wird also über das AWK - Skript suchen.awk realisiert (Listing 3)
Listing 3: suchen.awk
BEGIN {FS="\^"}
{ if ( toupper($FELD) == toupper(SUCHNAME) ) {
print"<TD>"$1"<TD>"$2"<TD>"$3"<TD>"$4"<TD>"$5"<td>"$6
"<TD>"$7"<TD><A HREF=MAILTO:"$8">"$8"</A><TD>
<A HREF="$9">"$9"</A><TR>"}
}
In der ersten Zeile des AWK - Skriptes wird dem Skript der Feldtrenner (Fieldseperator) mitgeteilt, welcher die einzelnen Felder voneinander abtrennt.
In den folgenden Zeilen findet dann der Vergleich gemäß den eingetragenen Suchkriterien statt. Hier wird jedes Feld des gewählten Typs (in Variable FELD) mit dem eingegebenen Suchbegriff verglichen und bei Gleichheit in die Ausgabetabelle geschrieben. Dank der toupper Kommandos läuft die Suche Case-insensitiev ab, d.h. Groß- und Kleinschreibung werden nicht berücksichtigt. Dabei werden die Emailadressen und Homepage - URL's als Links realisiert.
Letztendlich sieht die Suche im Zusammenspiel der Skripte suchen.cgi und suchen.awk dann folgendermaßen aus:
Die Eingabefunktion ist eine der einfachsten Funktionen innerhalb des kompletten Programmes. Sie ist im Modul ao-eintragen.html (Listing 4) und ao-adrwrite.cgi (Listing 5) umgesetzt. Das HTML Dokument ao-eintragen.html erzeugt dabei lediglich ein Formular mit der Eingabemaske und den entsprechenden action-Verweis auf das Auswertende CGI - Skript ao-adrwrite.cgi.
Listing 4: ao-eintragen.html
<title>Adressen Online: Eintragen</title>
<form action=ao-adrwrite.cgi method=get>
<table border>
<td>
Nachname:
<td>
<input type=text name=nachname size=30>
<tr>
<td>
Vorname:
<td>
<input type=text name=vorname size=30>
<tr>
<td>
Strasse:
<td>
<input type=text name=strasse size=30>
<tr>
<td>
Plz:
<td>
<input type=text name=plz size=10>
<tr>
<td>
Wohnort:
<td>
<input type=text name=ort size=30>
<tr>
<td>
Telefon (privat):
<td>
<input type=text name=privtel size=30>
<tr>
<td>
Fax (privat):
<td>
<input type=text name=privfax size=30>
<tR>
<td>
eMail:
<td>
<input type=text name=email size=30>
<tr>
<td>
Homepage:
<td>
<input type=text name=homepage size=40 value="http://">
<tR>
</table>
<input type=submit value="Speichern">
<input type=reset value="Loeschen">
</form>
Das CGI - Skript ao-adrwrite.cgi nimmt dabei die Eingaben des Fomulares entgegen und führt als erstes Validitätsprüfungen der einzelnen Felder durch (if Anweisungen). Dabei wird geprüft ob die Felder Daten enthalten oder Leer sind. Sind die Felder leer, so wird diesen der Wert "./." zugewiesen, da leere Felder innerhalb der ASCII Datei unter Umständen zu Problemen führen könnten.
Anschließend werden die eingegebenen Informationen in die entsprechende Syntaktische Form gebracht und in die ASCII Datei geschrieben. Dies erledigt die Zeile:
echo "$NACHNAME^$VORNAME^$STRASSE^$PLZ^$ORT^$TEL^$FAX^
$EMAIL^$HOMEPAGE^" >> $datenfile
Im Anschluß wird dann noch ein Skript (ao-db-sort.cgi / Listing 6) zum Sortieren der Datenbank aufgerufen und wieder auf die Startseite verwiesen.
Listing 5: ao-adrwrite.cgi
#!/bin/sh
###########################################################################
##
## Modul: Adressen Online - Datensatz einfuegen (schreiben)
## Autor: Achim Schmidt (as@saar.de)
##
##
##### Voreinstellungen #####
datenfile=`cat ao-dbpath.cfg`
### Webdaten uebernehmen ###
NACHNAME=`./cgiparse -value nachname`
VORNAME=`./cgiparse -value vorname`
STRASSE=`./cgiparse -value strasse`
PLZ=`./cgiparse -value plz`
ORT=`./cgiparse -value ort`
TEL=`./cgiparse -value privtel`
FAX=`./cgiparse -value privfax`
EMAIL=`./cgiparse -value email`
HOMEPAGE=`./cgiparse -value homepage`
### Validitaetspruefung ###
if test "$NACHNAME"
then
NACHNAME=$NACHNAME
else
NACHNAME="./."
fi
if test "$VORNAME"
then
VORNAME=$VORNAME
else
VORNAME="./."
fi
if test "$STRASSE"
then
STRASSE=$STRASSE
else
STRASSE="./."
fi
if test "$PLZ"
then
PLZ=$PLZ
else
PLZ="./."
fi
if test "$ORT"
then
ORT=$ORT
else
ORT="./."
fi
if test "$TEL"
then
TEL=$TEL
else
TEL="./."
fi
if test "$FAX"
then
FAX=$FAX
else
FAX="./."
fi
if test "$EMAIL"
then
EMAIL=$EMAIL
else
EMAIL="./."
fi
if test "$HOMEPAGE"
then
HOMEPAGE=$HOMEPAGE
else
HOMEPAGE="http://"
fi
echo "$NACHNAME^$VORNAME^$STRASSE^$PLZ^$ORT^$TEL^$FAX^$EMAIL^$HOMEPAGE^" >> $datenfile
./ao-db-sort.cgi
Das Sktipt ao-db-sort.cgi (Listing 6) führt die Sortierung des Datenfiles durch. Dies ist keine notwendige Maßnahme, erleichtert aber die lesbarkeit der Ausgabetabellen.
Das Skript läßt das Datenfile einfach durch die sort Anweisung laufen und speichert die Ausgaben in einer Temporären Datei, welche anschließend das Datenfile ersetzt und gelöscht wird.
Listing 6: ao-db-sort.cgi
#!/bin/sh
datenfile=`cat ao-dbpath.cfg`
cat $datenfile | sort > /tmp/adrol.sort
cp /tmp/adrol.sort $datenfile
rm /tmp/adrol.sort
echo "Location: ./"
echo
Um Datensätze aus dem bestehenden Datenfile zu löschen, muss zunächst der zu löschende Eintrag selektriert werden. Ich habe hier für eine zweistufige Selektion entschieden, da diese das Gesamtsystem etwas vereinfacht. In ersten Selektionsschritt wird praktisch eine ganz normale Suche innerhalb der Datendatei durchgeführt, jedoch als zusätzliches Feld noch die Datensatznummer angegeben. Der zweite Auswahlschritt ist dann die Eingabe der Datensatznummer. Ich habe auch die Bewußt eine Tastatureingabe vor einer 'Anklickversion' vorgezogen, da dies Fehleingaben oder 'Schnellschüsse' vermindert werden.
Anschließend wird der entsprechende Datensatz mittels eines sed (Streamedit) Kommandos gelöscht.
Die erste Selektion läuft im HTML Skript ao-loeschen.html (Listing 7) ab. Das HTML Skript stellt dabei wieder eine Maske dar, wie sie schon von der Suchfunktion her bekannt ist und ruft eine leicht modifizierte Suchfunktion (löschen.cgi, Listing 8) auf.
Desweiteren übergibt Sie dem CGI Skript ein weiteres Feld, welches vom Benutzer nicht beeinflußt werden kann. Es handelt sich dabei um das Feld kz (von Kennzeichen), welchem beim Löschen eine feste Null zugeordnet ist. Dies ist von Nöten, da ich dieses Skript auch zum späteren Ändern der Datensätze benutze.
Listing 7: ao-loeschen.html
<HTML>
<TITLE>ADDRESS ONLINE</TITLE>
<center>
<h2>Datensatz loeschen</h2>
</center>
<hr>
<center>
<form action=loeschen.cgi method=get>
<input type=hidden name=kz value=0>
Suchbegriff: <input type=text name=suchname size=30>
Suchen im Feld: <select NAME=feld>
<OPTION value=1 selected>Nachname
<OPTION value=2>Vorname
<OPTION value=3>Strasse
<OPTION value=4>Plz
<OPTION value=5>Ort
<OPTION value=6>Telefonnummer
<OPTION value=7>Faxnummer
<OPTION value=8>Email
<OPTION value=9>Homepage
</SELECT>
<input type=submit value="Suche starten">
</form>
</center>
<hr>
<a href=./>HAUPTMENUE</a>
<hr>
<address><font size=2>ADDRESS ONLINE (c) Achim Schmidt</font></address>
Im Programm loeschen.cgi (Listing 8) werden die Übergaben des HTML Dokumentes entgegengenommen, der Tabellenanfang ausgegeben und die Suche mittels des modifizierten AWK - Suchskriptes (suchen-1.awk) eingeleitet.
Zum Schluß dieses Modules wird noch das Tabellenende ausgegeben, sowie das Formular zur Eingabeaufforderung der Datensatznummer generiert. An Hand der Variablen AKZ wird bei der Formulargeneration unterschieden ob die Selektion an das eigentliche Lösch oder Änderungsmodul geleitet werden soll.
Listing 8: loeschen.cgi
#!/bin/sh
datenfile=`cat ao-dbpath.cfg`
SUCHNAME=`./cgiparse -value suchname`
AKZ=`./cgiparse -value kz`
FELD=`./cgiparse -value feld`
echo "Content-type: text/html"
echo
echo "<TITLE>ADRESS ONLINE</TITLE>"
echo "<CENTER><H3>Suchen nach: $SUCHNAME</H3></CENTER><HR><p>"
echo "<CENTER>"
echo "<TABLE BORDER>"
echo "<TD>DS-Nr.<TD>NACHNAME<TD>VORNAME<TD>STRASSE<TD>PLZ<TD>ORT<TD>TELEFON<TD>FAX<TD>EMAIL<TD>HOMEPAGE<TD><TR>"
awk -f suchen-1.awk FELD=$FELD SUCHNAME=$SUCHNAME $datenfile
echo "</TABLE>"
echo "</CENTER>"
echo "<p>"
echo "<center>"
if test $AKZ -eq 0
then
echo "<FORM action=delfromdb.cgi method=get>"
else
echo "<FORM action=changedb.cgi method=get>"
fi
echo "Loesche / Aendere Datensatz Nummer: <input type=text name=loeschds size=3>"
echo "<input type=submit value=\"OK\">"
echo "</form></center>"
echo "<p>"
echo "<HR>"
echo "<A HREF=./>HAUPTMENUE</A>"
echo "<HR><FONT SIZE=2>"
echo "<ADDRESS>ADDRESS ONLINE (c) Achim Schmidt</ADDRESS></FONT>"
Wird die Löschfunktion (wie derzeit ja erläutert) gewählt, so wird die Auswahl des Datensatzes an das CGI Skript delfromdb.cgi (Listing 9) übergeben, welches das eigentliche Löschen des Datensatzes erledigt.
Das löschen des selektierten Datensatzes wird mittels des sed umgesetzt. Hier wird einfach die selektierte Zeile des Datenfiles (entspricht der Datensatznummer) an den Parameter d des sed übergeben und dessen Befehlsausgabe in eine temporäre Datei geleitet. Diese wird anschließend auf das Datenfilekopiert und anschließend gelöscht.
Zum Schluß wird via Location - Anweisung wieder auf das Hauptmenue verwiesen.
Listing 9: delfromdb.cgi
#!/bin/sh
datenfile=`cat ao-dbpath.cfg`
tmpfile=/tmp/aotmp.$$
dsatz=`./cgiparse -value loeschds`
sed -e $dsatz'd' $datenfile > $tmpfile
cp $tmpfile $datenfile
rm $tmpfile
echo "Location: ./"
echo
Die Änderungsfunktion besteht eigentlich aus drei bekannten Funktionen. Dies ist einmal die Suchfunktion, die Löschfunktion und die Eingabefunktion.
Der Ablauf sieht folgendermaßen aus:
Da es sich dabei um mehrere existierende Teile handelt, wird auf die existierenden Funktionen entsprechend zurückgeriffen. Dies erspart zum einen eine Menge Schreibarbeit als auch Plattenplatz. Eingeleitet wird die Änderungsfunktion vom HTML - Skript ao-aendern.html (Listing 10), welches wieder die erste Suchmaske der zweiteiligen Selektion darstellt. Nach Betätigen des Submit - Buttons wird wiederum das CGI - Sktipt löschen aufgerufen, diesmal jedoch mit dem Kennzeichen 1 (kz=1).
Die Suchbedingungen werden nun wiederum vom Skript löschen.cgi (Listing 8) aufbereitet, dargestellt und die zweite Selektion (Datensatznummer) entgegengenommen. Diese Werte werden nun jedoch aufgrund des anderen Wertes der Variablen AKZ (AKZ=1) an das CGI Sktipt changedb.cgi (Listing 11) weitergeleitet.
Listing 10: ao-aendern.html
<HTML>
<TITLE>ADDRESS ONLINE</TITLE>
<center>
<h2>Datensatz aendern</h2>
</center>
<hr>
<center>
<form action=loeschen.cgi method=get>
<input type=hidden name=kz value=1>
Suchbegriff: <input type=text name=suchname size=30>
Suchen im Feld: <select NAME=feld>
<OPTION value=1 selected>Nachname
<OPTION value=2>Vorname
<OPTION value=3>Strasse
<OPTION value=4>Plz
<OPTION value=5>Ort
<OPTION value=6>Telefonnummer
<OPTION value=7>Faxnummer
<OPTION value=8>Email
<OPTION value=9>Homepage
</SELECT>
<input type=submit value="Suche starten">
</form>
</center>
<hr>
<a href=./>HAUPTMENUE</a>
<hr>
<address><font size=2>ADDRESS ONLINE (c) Achim Schmidt</font></address>
Im Modul changedb.cgi (Listing 11) werden nun die Eingaben aus loeschen.cgi entgegengenommen und in Shellvariablen gespeichert. Anschließend werden die Datensatzinhalte des selektierten Datensatzes mittels implementierten AWK Aufrufen in Shellvariablen gerettet. Der nächste Schritt ist nun das Löschen des selektierten Datensatzes (analog realisiert wie in der Löschfunktion).
Nun folgt die Darstellung der Eingabemaske, mit dem Unterschied, daß nun die einzelnen Felder mit den geretteten Werten aus den Shellvariablen vorbelgt sind und editiert werden können.
Beim Abschluß des Editiervorganges wird nun das Skript ao-adrwrite.cgi (Listing 5) wiederum aufgerufen, welches ja bereits aus der Eingabefunktion her bekannt ist.
Listing 11: changedb.cgi
#!/bin/sh
datenfile=`cat ao-dbpath.cfg`
tmpfile=/tmp/aotmp.$$
dsatz=`./cgiparse -value loeschds`
NAME=`awk 'BEGIN {FS="^"}{if ( NR == zeile ) {print $1}}' zeile=$dsatz $datenfile`
VORNAME=`awk 'BEGIN {FS="^"}{if ( NR == zeile ) {print $2}}' zeile=$dsatz $datenfile`
STRASSE=`awk 'BEGIN {FS="^"}{if ( NR == zeile ) {print $3}}' zeile=$dsatz $datenfile`
PLZ=`awk 'BEGIN {FS="^"}{if ( NR == zeile ) {print $4}}' zeile=$dsatz $datenfile`
ORT=`awk 'BEGIN {FS="^"}{if ( NR == zeile ) {print $5}}' zeile=$dsatz $datenfile`
TEL=`awk 'BEGIN {FS="^"}{if ( NR == zeile ) {print $6}}' zeile=$dsatz $datenfile`
FAX=`awk 'BEGIN {FS="^"}{if ( NR == zeile ) {print $7}}' zeile=$dsatz $datenfile`
EMAIL=`awk 'BEGIN {FS="^"}{if ( NR == zeile ) {print $8}}' zeile=$dsatz $datenfile`
HOMEPAGE=`awk 'BEGIN {FS="^"}{if ( NR == zeile ) {print $9}}' zeile=$dsatz $datenfile`
sed -e $dsatz'd' $datenfile > $tmpfile
cp $tmpfile $datenfile
rm $tmpfile
echo "Content-type: text/html"
echo
echo "<TITLE>ADDRESS ONLINE</TITLE>"
echo "<center><h2>Datensatz aendern</h2></center>"
echo "<hR>"
echo "<form action=ao-adrwrite.cgi mathod=get>"
echo "<center>"
echo "<table border>"
echo "<TD>Nachname:<TD><input type=text name=nachname size=60 value=\"$NAME\"><tr>"
echo "<TD>Vorname:<TD><input type=text name=vorname size=60 value=\"$VORNAME\"><tr>"
echo "<TD>Strasse:<TD><input type=text name=strasse size=60 value=\"$STRASSE\"><tr>"
echo "<TD>Plz:<TD><input type=text name=plz size=10 value=\"$PLZ\"><tr>"
echo "<TD>Ort:<TD><input type=text name=ort size=60 value=\"$ORT\"><tR>"
echo "<TD>Telefon:<TD><input type=text name=privtel size=60 value=\"$TEL\"><TR>"
echo "<TD>Fax:<TD><input type=text name=privfax size=60 value=\"$FAX\"><TR>"
echo "<TD>Email:<TD><input type=text name=email size=60 value=\"$EMAIL\"><TR>"
echo "<TD>Homepage:<TD><input type=text name=homepage size=60 value=\"$HOMEPAGE\"><TR>"
echo "</table>"
echo "<p>"
echo "<input type=submit value=\"Datensatz speichern\">"
echo "</form>"
echo "</center>"
echo "<p><hr><address><font size=2>ADDRESS ONLINE (c) Achim Schmidt</font></address>"
Wie Sie sehen, ist mittels CGI vieles auf recht simple Art realisierbar. Wenn Sie das System einmal testen oder sehen möchten so können Sie dies auf meiner Homepage unter der URL:
http://www.saar.de/as/publications/linuxmagazin/cgi/bsp/ao/
gerne einmal tun.
© 1996 by Achim Schmidt & Linuxmagazin