Kapitel 12. Programmierung

Inhaltsverzeichnis

12.1. Das Shellskript
12.1.1. POSIX-Shell-Kompatibilität
12.1.2. Shellparameter
12.1.3. Bedingte Ausdrücke in der Shell
12.1.4. Shellschleifen
12.1.5. Befehlsabfolge auf der Shell
12.1.6. Hilfsprogramme für Shellskripte
12.1.7. Shellskript-Dialog
12.1.8. Shellskript-Beispiel mit zenity
12.2. Make
12.3. C
12.3.1. Ein einfaches C-Programm (gcc)
12.4. Fehlersuche (Debugging)
12.4.1. Grundlegende Ausführung von gdb
12.4.2. Fehlersuche in einem Debian-Paket
12.4.3. Gewinnen von Backtrace-Informationen
12.4.4. Erweiterte gdb-Befehle
12.4.5. Fehleranalyse bei X-Fehlern
12.4.6. Überprüfen der Abhängigkeiten von Bibliotheken
12.4.7. Werkzeuge zur Erkennung von Speicherlecks
12.4.8. Werkzeuge zur statischen Code-Analyse
12.4.9. Disassemblieren von Binärdateien
12.5. Flex - ein besseres Lex
12.6. Bison - ein besseres Yacc
12.7. Autoconf
12.7.1. Kompilieren und Installieren eines Programms
12.7.2. Deinstallation eines Programms
12.8. Verrücktheiten bei kurzen Perl-Skripten
12.9. Web
12.10. Die Quellcode-Übersetzung
12.11. Erstellen von Debian-Paketen

Ich gebe hier einige Startimpulse, um Programmierung im Debian-System so weit zu erlernen, dass zumindest der Quellcode aus den Debian-Quellpaketen zur Fehlersuche verfolgt werden kann. Hier einige erwähnenswerte Pakete und dazugehörige Dokumentation zur Programmierung.

Tabelle 12.1. Liste von Paketen für die Programmierung

Paket Popcon Größe Dokumentation
autoconf V:27, I:219 1898 "info autoconf" aus dem autoconf-doc-Paket
automake V:25, I:206 1699 "info automake" aus dem automake1.10-doc-Paket
bash V:845, I:999 5363 "info bash" aus dem bash-doc-Paket
bison V:15, I:127 2201 "info bison" aus dem bison-doc-Paket
cpp V:422, I:853 22 "info cpp" aus dem cpp-doc-Paket
ddd V:1, I:21 3628 "info ddd" aus dem ddd-doc-Paket
exuberant-ctags V:6, I:41 289 exuberant-ctags(1)
flex V:14, I:117 1288 "info flex" aus dem flex-doc-Paket
gawk V:275, I:326 1852 "info gawk" aus dem gawk-doc-Paket
gcc V:186, I:688 7 "info gcc" aus dem gcc-doc-Paket
gdb V:34, I:166 6300 "info gdb" aus dem gdb-doc-Paket
gettext V:56, I:358 6408 "info gettext" aus dem gettext-doc-Paket
gfortran V:8, I:63 2 "info gfortran" aus dem gfortran-doc-Paket (Fortran 95)
fpc I:5 42 fpc(1) bzw. die HTML-Variante aus dem fp-docs-Paket (Pascal)
glade V:1, I:16 3152 Hilfe verfügbar über das Menü (UI Builder)
libc6 V:918, I:998 10280 "info libc" aus dem glibc-doc- und glibc-doc-reference-Paket
make V:184, I:655 1291 "info make" aus dem make-doc-Paket
xutils-dev V:3, I:32 1432 imake(1), xmkmf(1) usw.
mawk V:554, I:997 198 mawk(1)
perl V:758, I:994 17566 perl(1) bzw. die HTML-Seiten aus dem perl-doc- oder perl-doc-html-Paket
python V:710, I:985 571 python(1) bzw. die HTML-Seiten aus dem python-doc-Paket
tcl8.4 V:17, I:202 167 tcl(3) und detaillierte Handbuchseiten (manpages) aus dem tcl8.4-doc-Paket
tk8.4 V:11, I:135 168 tk(3) und detaillierte Handbuchseiten (manpages) aus dem tk8.4-doc-Paket
ruby V:62, I:268 47 ruby(1) und eine interaktive Referenz aus dem ri-Paket
vim V:148, I:379 2063 Hilfe-Menü (F1) aus dem vim-doc-Paket
susv2 I:0 39 die "Single UNIX Specifications v2"
susv3 I:0 39 die "Single UNIX Specifications v3"

Online-Referenzen sind verfügbar, indem Sie nach Installation der manpages- und manpages-dev-Pakete "man programmname" eingeben. Online-Referenzen für GNU-Werkzeuge erhalten Sie über "info programmname" nach Installation der entsprechenden Dokumentationspakete. Sie müssen unter Umständen die Bereiche contrib und non-free des Archivs zusätzlich zu main hinzufügen, da einige GFDL-Dokumentationen als nicht DFSG-frei angesehen werden.

[Warnung] Warnung

Verwenden Sie nicht "test" als Namen für eine ausführbare Testdatei. "test" ist ein fest integrierter Shell-Builtin.

[Achtung] Achtung

Sie sollten Software, die direkt aus den Quellen kompiliert wurden, in "/usr/local" oder "/opt" installieren, um Kollisionen mit Systemprogrammen zu vermeiden.

[Tipp] Tipp

Die Code-Beispiele zum Erzeugen des Songs "99 Bottles of Beer" sollten Ihnen gute Ideen zu nahezu allen Programmiersprachen liefern.

Das Shellskript ist eine Textdatei mit gesetztem Ausführungsbit und enthält Befehle in der folgenden Form:

#!/bin/sh
 ... Befehle

Die erste Zeile spezifiziert den Shell-Interpreter, der den Inhalt dieser Datei liest und ausführt.

Das Lesen von Shellskripten ist der beste Weg, um zu verstehen, wie ein Unix-artiges System arbeitet. Ich gebe hier einige Hinweise zur Shellprogrammierung. Lesen Sie "Shell Mistakes" (http://www.greenend.org.uk/rjk/2001/04/shell.html), um aus Fehlern zu lernen.

Anders als der interaktive Shell-Modus (lesen Sie Abschnitt 1.5, „Der einfache Shell-Befehl“ und Abschnitt 1.6, „Unix-ähnliche Textverarbeitung“) verwenden Shellskripte häufig Parameter, bedingte Ausdrücke und Schleifen.

Jeder Befehl gibt einen Beendigungs-Status (Exit-Status) zurück, der für einen bedingten Ausdruck verwendet werden kann:

  • Erfolg: 0 ("Wahr/True")

  • Fehler: nicht 0 ("Falsch/False")

[Anmerkung] Anmerkung

"0" im Kontext eines bedingten Ausdrucks für die Shell bedeutet "Wahr", während "0" im Kontext eines bedingten Ausdrucks für ein C-Programm "Falsch" bedeutet.

[Anmerkung] Anmerkung

"[" ist das Äquivalent des test-Befehls, das seine Argumente bis zum "]" als bedingten Ausdruck wertet.

Grundlegende Ausdrucksformen für bedingte Ausdrücke, die Sie sich einprägen sollten:

  • "<befehl> && <bei_erfolg_auch_diesen_befehl_ausführen> || true"

  • "<befehl> || <falls_kein_erfolg_auch_diesen_befehl_ausführen> || true"

  • ein mehrzeiliger Skriptschnipsel wie dieser:

if [ <bedingter_ausdruck> ]; then
 <bei_erfolg_diesen_befehl_ausführen>
else
 <falls_kein_erfolg_diesen_befehl_ausführen>
fi

Hierbei ist das "|| true" am Ende erforderlich, um sicherzustellen, dass das Shellskript sich nicht bei dieser Zeile beendet, wenn die Shell mit der "-e"-Option aufgerufen wurde.



Arithmetische Ganzzahlvergleicher in bedingten Ausdrücken sind "-eq", "-ne", "-lt", "-le", "-gt" und "-ge".

Die Shell verarbeitet ein Skript im Prinzip in der folgenden Abfolge:

  • Die Shell liest eine Zeile.

  • Die Shell gruppiert Teile der Zeile zu zusammengehörigen Ausdrücken (Token) zusammen, wenn diese sich innerhalb von "…" oder '…' befinden.

  • Die Shell splittet andere Teile der Zeile in einzelne Ausdrücke (Token) auf, wenn diese wie folgt von einander getrennt sind:

    • Whitespace-Zeichen: <Leerzeichen> <Tabulator> <newline>

    • Metazeichen: < > | ; & ( )

  • Die Shell prüft jeden Ausdruck auf Schlüsselworte (wenn nicht innerhalb von "…" oder '…'), um deren Verhalten anzupassen.

    • Schlüsselwörter sind: if then elif else fi for in while unless do done case esac

  • Die Shell expandiert Alias-Befehle (wenn nicht innerhalb von "…" oder '…').

  • Die Shell expandiert eine Tilde (wenn nicht innerhalb von "…" oder '…'):

    • "~" → Heimatverzeichnis des aktuellen Benutzers

    • "~<benutzer>" → Heimatverzeichnis von <benutzer>

  • Die Shell expandiert Parameter in deren Wert (wenn nicht innerhalb von '…'):

    • Parameter: "$PARAMETER" oder "${PARAMETER}"

  • Die Shell expandiert Befehlsersetzungen / command substitutions (wenn nicht innerhalb von '…'):

    • "$( befehl )" → die Ausgabe von "befehl"

    • "` befehl `" → die Ausgabe von "befehl"

  • Die Shell expandiert Pfadnamenmuster in die passenden Dateinamen (wenn nicht innerhalb von "…" oder '…'):

    • * → jegliche Zeichen (eins oder mehrere)

    • ? → irgendein (nur ein) Zeichen

    • […] → jegliche Zeichen von denen in ""

  • Die Shell sucht befehl in folgenden Definitionen und führt ihn aus:

    • Funktions-Definition

    • Builtin (integrierter Befehl)

    • ausführbare Datei in "$PATH"

  • Die Shell geht zur nächsten Zeile und wiederholt diesen kompletten Ablauf vom Anfang.

Einfache Anführungszeichen innerhalb von doppelten Anführungszeichen haben keine Wirkung.

Das Ausführen von "set -x" in der Shell oder das Aufrufen einer Shell mit der Option "-x" veranlasst die Shell, alle ausgeführten Befehle auch auf dem Bildschirm auszugeben. Dies ist sehr nützlich zur Fehlersuche.

Hier ist ein einfaches Skript, das ein ISO-Image aus RS02-Daten erzeugt, welche von dvdisaster(1) bereitgestellt werden.

#!/bin/sh -e
# gmkrs02 : Copyright (C) 2007 Osamu Aoki <osamu@debian.org>, Public Domain
#set -x
error_exit()
{
  echo "$1" >&2
  exit 1
}
# Variablen initialisieren
DATA_ISO="$HOME/Desktop/iso-$$.img"
LABEL=$(date +%Y%m%d-%H%M%S-%Z)
if [ $# != 0 ] && [ -d "$1" ]; then
  DATA_SRC="$1"
else
# Auswahl eines Verzeichnisses zur Erzeugung des ISO-Images
  DATA_SRC=$(zenity --file-selection --directory  \
    --title="Select the directory tree root to create ISO image") \
    || error_exit "Exit on directory selection"
fi
# Größe des Archivs überprüfen
xterm -T "Check size $DATA_SRC" -e du -s $DATA_SRC/*
SIZE=$(($(du -s $DATA_SRC | awk '{print $1}')/1024))
if [ $SIZE -le 520 ] ; then
  zenity --info --title="Dvdisaster RS02" --width 640  --height 400 \
    --text="The data size is good for CD backup:\\n $SIZE MB"
elif [ $SIZE -le 3500 ]; then
  zenity --info --title="Dvdisaster RS02" --width 640  --height 400 \
    --text="The data size is good for DVD backup :\\n $SIZE MB"
else
  zenity --info --title="Dvdisaster RS02" --width 640  --height 400 \
    --text="The data size is too big to backup : $SIZE MB"
  error_exit "The data size is too big to backup :\\n $SIZE MB"
fi
# Nur xterm hat eine sicher funktionierende -e-Option
# Rohes ISO-Image erzeugen
rm -f "$DATA_ISO" || true
xterm -T "genisoimage $DATA_ISO" \
  -e genisoimage -r -J -V "$LABEL" -o "$DATA_ISO" "$DATA_SRC"
# Eine zusätzliche RS02-Redundanz erzeugen
xterm -T "dvdisaster $DATA_ISO" -e  dvdisaster -i "$DATA_ISO" -mRS02 -c
zenity --info --title="Dvdisaster RS02" --width 640  --height 400 \
  --text="ISO/RS02 data ($SIZE MB) \\n created at: $DATA_ISO"
# EOF (Dateiende)

Sie möchten vielleicht für solch ein Skript einen Starter auf der Arbeitsfläche erstellen; nutzen Sie etwas wie "/usr/local/bin/gmkrs02 %d".

Make ist ein Werkzeug, um Gruppen von Programmen zu betreuen. Bei Ausführung des Befehls make(1) liest make die Regeldatei "Makefile" und aktualisiert ein Ziel (Target), falls sich Dateien, von denen das Makefile abhängt, seit der letzten Modifizierung des Targets verändert haben oder falls das Target nicht existiert. Die Ausführungen dieser Aktualisierungen können zeitgleich erfolgen.

Die Syntax der Regeldatei ist folgende:

target: [ Voraussetzungen ... ]
 [TAB]  befehl1
 [TAB]  -befehl2 # Fehler ignorieren
 [TAB]  @befehl3 # Ausgabe unterdrücken

Hierbei ist "[TAB]" ein TAB-Code. Jede Zeile wird nach Ersetzung der Variablen durch die Shell interpretiert. Verwenden Sie "\" am Ende einer Zeile, um das Skript fortzusetzen. Zur Angabe von Umgebungsvariablen ist statt "$" hier "$$" zu schreiben.

Implizite Regeln für das Target und Voraussetzungen können z.B. wie folgt angegeben werden:

%.o: %.c header.h

Hier enthält das Target das Zeichen "%" (exakt eines davon). Das "%" passt auf jeden nicht leeren Teil-String in den eigentlichen Dateinamen des Targets. Auch die Voraussetzungen nutzen auf ähnliche Art ein "%", um den Bezug zum Namen des Targets anzuzeigen.



Führen Sie "make -p -f/dev/null" aus, um alle internen automatischen Regeln zu sehen.

Sie können wie folgt eine korrekte Umgebung zum Kompilieren von in der C-Programmiersprache geschriebenen Programmen einrichten:

# apt-get install glibc-doc manpages-dev libc6-dev gcc build-essential

Das Paket libc6-dev (d.h. die GNU-C-Bibliothek) bietet als C-Standard-Bibliothek eine Sammlung von Header-Dateien und Bibliotheksroutinen, die von der C-Sprache genutzt werden.

Referenzen für C finden Sie über:

  • "info libc" (Referenz für Funktionen der C-Bibliothek)

  • gcc(1) und "info gcc"

  • jeglicher_funktionsname_aus_der_c_bibliothek(3)

  • Kernighan & Ritchie, "The C Programming Language", 2. Ausgabe (Prentice Hall)

Debugging ist ein wichtiger Teil der Programmieraktivitäten. Das Wissen darüber, wie man in Programmen einen Fehler sucht, macht Sie zu einem guten Debian-Nutzer, der aussagekräftige Fehlerberichte erstellen kann.

Das primäre Programm zur Fehlersuche (Debugger) im Debian-System ist gdb(1), welches Ihnen erlaubt, ein Programm zu inspizieren, während es läuft.

Wir installieren gdb und zugehörige Programme wie folgt:

# apt-get install gdb gdb-doc build-essential devscripts

Eine gute Einführung zu gdb erhalten Sie mit "info gdb" oder im Netz. Hier ein einfaches Beispiel zur Verwendung von gdb(1) bei einem Programm namens "program", kompiliert mit der Option "-g", um Debugging-Informationen auszugeben.

$ gdb program
(gdb) b 1                # Haltepunkt in Zeile 1 setzen
(gdb) run args           # Programm mit Argumenten ausführen
(gdb) next               # nächste Zeile
...
(gdb) step               # einen Schritt vorwärts
...
(gdb) p parm             # parm ausgeben
...
(gdb) p parm=12          # Wert auf 12 setzen
...
(gdb) quit
[Tipp] Tipp

Viele gdb(1)-Befehle können abgekürzt werden. Vervollständigungen funktionieren wie in der Shell mit der Tabulator-Taste.

Flex ist ein Lex-kompatibler schneller lexikalischer Analysegenerator.

Eine Einführung zu flex(1) finden Sie in "info flex".

Sie müssen Ihre eigenen "main()"- und "yywrap()"-Funktionen bereitstellen oder Ihr Programm sollte wie folgt aussehen, um ohne eine Bibliothek zu kompilieren (dies ist so, weil "yywrap" ein Makro ist und "%option main" implizit "%option noyywrap" aktiviert):

%option main
%%
.|\n    ECHO ;
%%

Alternativ könnten Sie mit der "-lfl"-Linker-Option am Ende Ihrer cc(1)-Kommandozeile kompilieren (wie AT&T-Lex mit "-ll"). Es wird in diesem Fall kein "%option" benötigt.

Einige Pakete stellen Yacc-kompatible LR-Parser- oder LALR-Parser-Generatoren in Debian bereit:


Eine Einführung zu bison(1) finden Sie in "info bison".

Sie müssen Ihre eigenen "main()"- und "yyerror()"-Funktionen bereitstellen. "main()" ruft "yyparse()" auf, das wiederum "yylex()" aufruft, was gewöhnlich von FleX erzeugt wird.

%%

%%

Autoconf ist ein Werkzeug zum Erzeugen von Shellskripten, welche Quellpakete automatisch so konfigurieren, dass sie sich unter Verwendung des vollständigen GNU-Build-Systems an viele UNIX-artige Systeme anpassen.

autoconf(1) erzeugt das Konfigurationsskript "configure". "configure" erzeugt automatisch ein angepasstes "Makefile" aus der "Makefile.in"-Vorlage.

Obwohl man jedes AWK-Skript mittels a2p(1) automatisiert nach Perl umschreiben kann, werden einzeilige AWK-Skripte am besten per Hand nach Perl konvertiert.

Denken wir an folgende AWK-Skriptzeile:

awk '($2=="1957") { print $3 }' |

Sie ist äquivalent zu jeder der folgenden Zeilen:

perl -ne '@f=split; if ($f[1] eq "1957") { print "$f[2]\n"}' |
perl -ne 'if ((@f=split)[1] eq "1957") { print "$f[2]\n"}' |
perl -ne '@f=split; print $f[2] if ( $f[1]==1957 )' |
perl -lane 'print $F[2] if $F[1] eq "1957"' |
perl -lane 'print$F[2]if$F[1]eq+1957' |

Das letzte ist eine Knobelaufgabe. Es nutzt die Vorteile folgender Perl-Funktionalitäten:

  • Der Whitespace ist optional.

  • Es existiert eine automatische Konvertierung von Zahlen zu Zeichenketten.

Details zu den Befehlszeilenoptionen finden Sie in perlrun(1). Für noch verrücktere Perl-Skripte könnte Perl Golf interessant sein.

Einfache interaktive dynamische Webseiten können wie folgt erstellt werden:

  • Abfragen werden mittels HTML-Formularen dem Browser-Nutzer präsentiert.

  • Das Ausfüllen und Anklicken von Formulareinträgen sendet einen URL-String mit kodierten Parametern vom Browser zum Webserver:

    • "http://www.foo.dom/cgi-bin/program.pl?WERT1=WERT1&WERT2=WERT2&WERT3=WERT3"

    • "http://www.foo.dom/cgi-bin/program.py?VAR1=WERT1&VAR2=WERT2&VAR3=WERT3"

    • "http://www.foo.dom/program.php?VAR1=WERT1&VAR2=WERT2&VAR3=WERT3"

  • "%nn" in einer URL wird durch ein Zeichen mit hexadezimalem nn-Wert ersetzt.

  • Die Umgebungsvariable wird gesetzt als: "ABFRAGE_STRING="VAR1=WERT1 VAR2=WERT2 VAR3=WERT3"".

  • Ein CGI-Programm (irgendeines von "program.*") auf dem Webserver führt sich selbst mit der Umgebungsvariable "$ABFRAGE_STRING" aus.

  • Die Standardausgabe (stdout) eines CGI-Programms wird zum Webbrowser gesandt und dort als interaktive dynamische Webseite angezeigt.

Aus Sicherheitsgründen wird empfohlen, keinen eigenen zusammengebastelten Code zum Parsen von CGI-Parametern zu verwenden. Es gibt dafür etablierte Module in Perl und Python. PHP unterstützt diese Funktionalitäten. Wenn eine Speicherung der Daten auf dem Client nötig ist, werden HTTP-Cookies verwendet. Ist eine Verarbeitung der Daten auf dem Client erforderlich, wird häufig Javascript genutzt.

Für weitere Informationen wird auf das Common Gateway Interface, die Apache Software Foundation und JavaScript verwiesen.

Die Suche nach "CGI tutorial" auf Google durch Eingabe der kodierten URL http://www.google.com/search?hl=en&ie=UTF-8&q=CGI+tutorial direkt in der Adresszeile des Browsers ist eine gute Möglichkeit, das CGI-Skript auf dem Google-Server in Aktion zu beobachten.

Es gibt verschiedene Programme zur Übersetzung von Quellcode:


Wenn Sie ein Debian-Paket erstellen möchten, lesen Sie folgendes:

Es gibt auch Pakete wie dh-make, dh-make-perl usw., die beim Paketieren helfen.