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.2. Scripting in interpreted languages
12.2.1. Debugging interpreted language codes
12.2.2. GUI program with the shell script
12.2.3. Verrücktheiten bei kurzen Perl-Skripten
12.3. Coding in compiled languages
12.3.1. C
12.3.2. Ein einfaches C-Programm (gcc)
12.3.3. Flex - ein besseres Lex
12.3.4. Bison - ein besseres Yacc
12.4. Werkzeuge zur statischen Code-Analyse
12.5. Fehlersuche (Debugging)
12.5.1. Grundlegende Ausführung von gdb
12.5.2. Fehlersuche in einem Debian-Paket (Debugging)
12.5.3. Gewinnen von Backtrace-Informationen
12.5.4. Erweiterte gdb-Befehle
12.5.5. Überprüfen der Abhängigkeiten von Bibliotheken
12.5.6. Dynamic call tracing tools
12.5.7. Fehleranalyse bei X-Fehlern
12.5.8. Werkzeuge zur Erkennung von Speicherlecks
12.5.9. Disassemblieren von Binärdateien
12.6. Build tools
12.6.1. Make
12.6.2. Autotools
12.6.2.1. Kompilieren und Installieren eines Programms
12.6.2.2. Deinstallation eines Programms
12.6.3. Meson
12.7. Web
12.8. Die Quellcode-Übersetzung
12.9. Erstellen von Debian-Paketen

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

Handbuchseiten (manpages) sind verfügbar, indem Sie nach Installation der manpages- und manpages-dev-Pakete "man programmname" eingeben. Informationen über 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 freigeben, da einige GFDL-Dokumentation als nicht DFSG-frei angesehen wird.

Please consider to use version control system tools. See Abschnitt 10.5, „Git“.

[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
 ... command lines

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; Argumente bis zum "]" werden als bedingter Ausdruck gewertet.

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 [ conditional_expression ]; then
 if_success_run_this_command
else
 if_not_success_run_this_command
fi

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



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

    • Metacharacters: | ; & ( )

  • 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.


When you wish to automate a task on Debian, you should script it with an interpreted language first. The guide line for the choice of the interpreted language is:

  • Use dash, if the task is a simple one which combines CLI programs with a shell program.

  • Use python3, if the task isn't a simple one and you are writing it from scratch.

  • Use perl, tcl, ruby, ... if there is an existing code using one of these languages on Debian which needs to be touched up to do the task.

If the resulting code is too slow, you can rewrite only the critical portion for the execution speed in a compiled language and call it from the interpreted language.

The shell script can be improved to create an attractive GUI program. The trick is to use one of so-called dialog programs instead of dull interaction using echo and read commands.


Here is an example of GUI program to demonstrate how easy it is just with a shell script.

This script uses zenity to select a file (default /etc/motd) and display it.

GUI launcher for this script can be created following Abschnitt 9.4.10, „Ein Programm aus der grafischen Oberfläche heraus starten“.

#!/bin/sh -e
# Copyright (C) 2021 Osamu Aoki <osamu@debian.org>, Public Domain
# vim:set sw=2 sts=2 et:
DATA_FILE=$(zenity --file-selection --filename="/etc/motd" --title="Select a file to check") || \
  ( echo "E: File selection error" >&2 ; exit 1 )
# Check size of archive
if ( file -ib "$DATA_FILE" | grep -qe '^text/' ) ; then
  zenity --info --title="Check file: $DATA_FILE" --width 640  --height 400 \
    --text="$(head -n 20 "$DATA_FILE")"
else
  zenity --info --title="Check file: $DATA_FILE" --width 640  --height 400 \
    --text="The data is MIME=$(file -ib "$DATA_FILE")"
fi

This kind of approach to GUI program with the shell script is useful only for simple choice cases. If you are to write any program with complexities, please consider writing it on more capable platform.


Here, Abschnitt 12.3.3, „Flex - ein besseres Lex“ and Abschnitt 12.3.4, „Bison - ein besseres Yacc“ are included to indicate how compiler-like program can be written in C language by compiling higher level description into C language.

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)

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.

Lint like tools can help automatic static code analysis.

Indent like tools can help human code reviews by reformatting source codes consistently.

Ctags like tools can help human code reviews by generating an index (or tag) file of names found in source codes.

[Tipp] Tipp

Configuring your favorite editor (emacs or vim) to use asynchronous lint engine plugins helps your code writing. These plugins are getting very powerful by taking advantage of Language Server Protocol. Since they are moving fast, using their upstream code instead of Debian package may be a good option.


Debug is important part of programming activities. Knowing how to debug programs makes you a good Debian user who can produce meaningful bug reports.


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

Good tutorial of gdb can be found:

  • info gdb

  • “Debugging with GDB” in /usr/share/doc/gdb-doc/html/gdb/index.html

  • tutorial on the web

Here is a simple example of using gdb(1) on a "program" compiled with the "-g" option to produce debugging information.

$ gdb program
(gdb) b 1                # set break point at line 1
(gdb) run args           # run program with args
(gdb) next               # next line
...
(gdb) step               # step forward
...
(gdb) p parm             # print parm
...
(gdb) p parm=12          # set value to 12
...
(gdb) quit
[Tipp] Tipp

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

Since all installed binaries should be stripped on the Debian system by default, most debugging symbols are removed in the normal package. In order to debug Debian packages with gdb(1), *-dbgsym packages need to be installed (e.g. coreutils-dbgsym in the case of coreutils). The source packages generate *-dbgsym packages automatically along with normal binary packages and those debug packages are placed separately in debian-debug archive. Please refer to articles on Debian Wiki for more information.

If a package to be debugged does not provide its *-dbgsym package, you need to install it after rebuilding it by the following.

$ mkdir /path/new ; cd /path/new
$ sudo apt-get update
$ sudo apt-get dist-upgrade
$ sudo apt-get install fakeroot devscripts build-essential
$ apt-get source package_name
$ cd package_name*
$ sudo apt-get build-dep ./

Beheben Sie die Fehler, falls erforderlich.

Erhöhen Sie die Paketversion auf eine Versionsbezeichnung, die nicht mit offiziellen Debian-Versionen kollidiert (Sie können z.B. ein "+debug1" anhängen, wenn Sie eine existierende Paketversion neu kompilieren, oder Sie hängen "~pre1" an, wenn Sie eine noch nicht veröffentlichte Paketversion selbst kompilieren). Verwenden Sie dazu:

$ dch -i

Kompilieren und installieren Sie ein Paket mit Debugging-Symbolen wie folgt:

$ export DEB_BUILD_OPTIONS="nostrip noopt"
$ debuild
$ cd ..
$ sudo debi package_name*.changes

Sie müssen die Build-Skripte des Pakets überprüfen und sicherstellen, dass "CFLAGS=-g -Wall" zum Kompilieren der Binärdateien verwendet wird.

Wenn Sie einen Programmabsturz erlitten haben, ist es eine gute Idee, einen Fehlerbericht einzureichen und an diesen per Kopieren-und-Einfügen Backtrace-Informationen (Daten zur Rückverfolgung von Vorgängen in Programmen zwecks Fehleranalyse) anzuhängen.

The backtrace can be obtained by gdb(1) using one of the following approaches:

For infinite loop or frozen keyboard situation, you can force to crash the program by pressing Ctrl-\ or Ctrl-C or executing “kill -ABRT PID”. (See Abschnitt 9.4.12, „Einen Prozess beenden (kill)“)

[Tipp] Tipp

Oft stellt man fest, dass in den Backtrace-Informationen eine oder mehrere der ersten Zeilen "malloc()" oder "g_malloc()" enthalten. Wenn dies passiert, besteht eine hohe Wahrscheinlichkeit, dass Ihr Backtrace nicht sehr nützlich sein wird. Der einfachste Weg nützliche Informationen zu bekommen ist, die Umgebungsvariable "$MALLOC_CHECK_" auf einen Wert von 2 zu setzen (Näheres in malloc(3)). Sie können dies wie folgt erledigen, während gdb läuft:

 $ MALLOC_CHECK_=2 gdb hello

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: [ prerequisites ... ]
 [TAB]  command1
 [TAB]  -command2 # ignore errors
 [TAB]  @command3 # suppress echoing

Hierbei ist "[TAB]" ein TAB-Code. Jede Zeile wird nach Ersetzung der Variablen durch die Shell interpretiert. Verwenden Sie "\" am Ende einer Zeile, um den Befehl in der nächsten Zeile fortzusetzen. Zur Angabe von Umgebungsvariablen müssen Sie statt "$" hier "$$" 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.

Autotools is a suite of programming tools designed to assist in making source code packages portable to many Unix-like systems.

  • Autoconf is a tool to produce a shell script "configure" from "configure.ac".

    • "configure" is used later to produce "Makefile" from "Makefile.in" template.

  • Automake is a tool to produce "Makefile.in" from "Makefile.am".

  • Libtool is a shell script to address the software portability problem when compiling shared libraries from source code.

The software build system has been evolving:

  • Autotools on the top of Make has been the de facto standard for the portable build infrastructure since 1990s. This is extremely slow.

  • CMake initially released in 2000 improved speed significantly but was still build on the top of inherently slow Make.

  • Ninja initially released in 2012 is meant to replace Make for the further improved build speed but is also designed to have its input files generated by a higher-level build system.

  • Meson initially released in 2013 is the new popular and fast higher-level build system which uses Ninja as its backend.

See documents found at "The Meson Build system" and "The Ninja build system".

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 debmake, dh-make, dh-make-perl usw., die beim Paketieren helfen.