Inhaltsverzeichnis

7. Shell-Programm, Programmiermodell und Lösungen

7.1 Wohin mit dem Programm – geeignete Ablageorte

Es ist ein ungeschriebenes Gesetz, dass Distributionen die Verzeichnisse unter /opt und /usr/local nicht anrühren.

Root:

Root sollte (z. B. aus dem Internet herunter geladene) Drittanbieter-Programme unter /opt installieren und nach /usr/local/bin (für normale Benutzer) oder /usr/local/sbin (Systemprogramme) verlinken.
Root sollte eigene Programme unter /usr/local/bin (für normale Benutzer) oder /usr/local/sbin (Systemprogramme) ablegen.
Die Shell-Variable PATH enthält standardmäßig /usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin. Mit dieser Suchreihenfolge werden (bei Programmen mit gleichen Namen) zuerst die eigenen, dann die (nach-)installierten, dann die Standard-Programme gefunden.

Andere Benutzer:

Nach dem Freedesktop-Standard ist $HOME/.local synonym zu /usr/local. Benutzer sollten eigene Programme unter $HOME/.local/bin ablegen und dieses Verzeichnis in der Shell-Variablen PATH angeben.
PATH=“~/.local/bin:/usr/local/bin:/usr/bin:/bin“
Dazugehörige Bibliotheken, Dokumentationen und Quelltexte (für Compiler) liegen dann unter $HOME/.local/lib, $HOME/.local/doc und $HOME/.local/src.

7.2 Die erste Zeile – Angabe des Interpreters

Hinweis: Dies ist eine Funktion des Kernels!

Ist eine Textdatei ausführbar, und beginnt die erste Zeile mit „#!“, wird alles was in der Zeile dahinter steht als zu verwendendes Interpreter-Programm interpretiert.

Beispiele:

#!/bin/sh
# Standard-Shell

#!/usr/bin/bash
# GNU Bash

#!/usr/bin/awk -f
# AWK-Programm

#!/usr/bin/bc -l
# BC-Programm

#!/usr/bin/sed -f
# SED-Programm

7.3 Programmiermodell

7.4 Mehrfachausführung verhindern – Lock-Datei

Unix ist ein Multitasking-System, es können beliebig viele Programme gleichzeitig ausgeführt werden. Soll ein Programm während der Ausführung den erneuten Aufruf verhindern, arbeitet es am besten mit Lock-Dateien.

Die Namenskonvention für Geräte-Lock-Dateien ist LCK.<device>, wobei <device> der Gerätename im Dateisystem ist.

Es wird das Format der HDU-UUCP-Lock-Dateien verwendet. Das heißt, Lock–Dateien enthalten eine Prozess-ID als 10-Byte-ASCII-Dezimalzahl (führende Leerzeichen), gefolgt von einem Zeilenumbruch.

Root:

Root sollte Lock-Dateien unter /var/local/lock speichern lassen.

Andere Benutzer:

Nach dem Freedesktop-Standard ist $HOME/.cache synonym zu /var. Benutzer können für Lock-Dateien das Verzeichnis $HOME/.cache/local/lock anlegen.

Beispiel für Lock-Datei schreiben:
mkdir -p /var/local/lock
printf „%10s“ $$ > /var/local/lock/$0.lock
echo » /var/local/lock/$0.lock

Beispiel für Lock-Datei erkennen und mit (frei erfundenem) Exit-Code >0 beenden:
[ -r “/var/local/lock/programmname.lock“ ] && exit 16

7.5 Konfiguration

Die Summe aller sichtbaren Shell-Variablen nennt man Umgebung. Diese Umgebung lässt sich üblicherweise in der Reihenfolge Programmierer ⇒ Root ⇒ Benutzer konfigurieren. Also:

Für den Programmablauf notwendige Variablen werden vom Programmierer vorbelegt.

Root kann diese Umgebung überschreiben oder zusätzliche Variablen festlegen. Die dazugehörigen Dateien liegen unter /etc/local.

Nach dem Freedesktop-Standard ist $HOME/.config synonym zu /etc. Benutzer können die Umgebung von Root individuell überschreiben oder zusätzliche Variablen festlegen, abgelegt z.B. unter $HOME/.config/local.

Beispiel:

Konfigurationsdatei /etc/local/prog.cfg von Root:
var1=“Wert von Root“
var2=“noch ein Wert von Root“

Konfigurationsdatei $HOME/.config/local/prog.cfg vom Benutzer:
var1=“Wert vom Benutzer“
var3=“noch ein Wert vom Benutzer“

Shell-Programm:
#!/bin/sh var1=“Wert vom Programmierer“
[ -r “/etc/local/prog.cfg“ ] && . /etc/local/prog.cfg
[ -r “~/config/local/prog.cfg“ ] && . /etc/local/prog.cfg
export var1
[ -n “$var2“ ] && export var2
[ -n “$var3“ ] && export var3

7.6 Wer führt das Programm aus? – Root oder Benutzer

Der Benutzer root hat per Definition die User-ID 0. Diese lässt sich mit dem Programm id abfragen.

if [ “$(id -u)“ = “0“ ] ; then
# Programmteil für Root
else
# Programmteil für andere Benutzer
fi

7.7 Kommandozeilenparameter und Auswertung

Jedes Programm hat ein Standard-Verhalten. Dieses Standard-Verhalten wird üblicherweise so gewählt, dass das Programm gefahrlos gestartet / ausprobiert werden kann, oft erfolgt lediglich die Ausgabe einer Meldung.

Abweichungen von diesem Standard-Verhalten erfordern zusätzliche optionale Angaben, die dem Programm dann über die Kommandozeilenparameter mitgeteilt werden.

Beispiel: Programm für die Steuerung des SSH Server-Dienstes:

#!/bin/sh
case $1 in
start)
if [ “$(id -u)“ = “0“ ] ; then
systemctl start ssh
else
echo “Es sind Root-Rechte erforderlich.“ >&2
fi
;;
stop)
if [ “$(id -u)“ = “0“ ] ; then
systemctl stop ssh
else
echo “Es sind Root-Rechte erforderlich.“ >&2
fi
;;
*)
systemctl status ssh
echo
echo „Benutzung: rc.ssh start|stop“
;;
esac

Dieses Beispiel-Programm gibt nur Meldungen aus, wenn kein Parameter „start“ oder „stop“ angegeben, oder wenn das Programm nicht mit Root-Rechte gestartet wurde.

7.7.1 Auswertung mit getopts-Kommando

Beispiel:

# Hier wird "$@" verwendet, um die Kommandozeilenparameter in einzelne
# Wörter zerlegt zu übergeben.
TEMP=$(getopt -o 'ab:c::' –long 'a-long,b-long:,c-long::' -n 'example.bash' – "$@")
if [ $? -ne 0 ]; then
 echo 'Beende mit Fehler…'>&2
 exit 1
fi
eval set – "$TEMP"
unset TEMP
while true; do
 case "$1" in
 '-a'|'–a-long')
  echo 'Option a'
  shift
  continue
 ;;
 '-b'|'–b-long')
  echo "Option b, Argument '$2'"
  shift 2
  continue
 ;;
 '-c'|'–c-long')
 # c hat ein optionales Argument.
  case "$2" in
  '')
   echo 'Option c, kein Argument'
  ;;
  *)
   echo "Option c, Argument '$2'"
  ;;
  esac
  shift 2
  continue
 ;;
 '–')
  shift
  break
 ;;
 *)
  echo 'Internal error!'>&2
  exit 1
 ;;
 esac
done

echo 'Übrig gebliebene Argumente:'
for arg; do
 echo "–> '$arg'"
done

7.8 Daten ein-/ausgeben, Fehlermeldungen

Für alle Arten von Nutzdaten sind die 3 Kanäle stdin (Standard-Eingabe Kanal 0), stdout (Standard-Ausgabe Kanal 1) und stderr (Standard-Fehlerausgabe Kanal 2) vorgesehen. Nur das Vordergrund-Programm ist normalerweise mit Tastatur und Display verbunden. Alle anderen Hintergrund-Programme nehmen keine Eingaben von der Tastatur entgegen, Ausgaben gehen normalerweise zum Pseudo-Gerät /dev/null. Diese normalen Verbindungen können von / nach Dateien umgelenkt werden.

7.8.1 Dateneingabe

Aufgerufene Kommandos lesen Daten von der Standard-Eingabe.

Das Programm read kann während der Programmausführung Daten in Shell-Variablen einlesen.

read var
echo $var

Mit dem Programm cat lässt sich ein einfacher zeilenorientierter Texteditor realisieren:

cat > Datei.txt « „EOF„
Schreibt gesamten Text zeilenweise in die Datei
bis das Label EOFalleinstehend erkannt wird.
Innerhalb der Zeile kann auch noch korrigiert werden.
EOF

7.8.2 Datenausgabe und Fehlermeldungen

Aufgerufene Kommandos schreiben Nutzdaten in die Standard-Ausgabe.

Die Shell kann über das Pipe-Zeichen „|“ die Ausgabe eines Kommandos mit der Standard-Eingabe des nächsten Kommandos verbinden. Fehlermeldungen sollten über den dafür vorgesehenen Kanal 2 ausgegeben werden, damit diese von dem per Pipe verbundenen Kommando nicht versehentlich als Nutzdaten interpretiert werden.

Hinweis: Die Ausgabe auf dem Display unterscheidet sich von Datenumlenkung oder Pipe-Verbindung.

Beispiele zum ausprobieren:

echo Datenausgabe auf Display
echo Fehlerausgabe auf Display >&2
echo -n „Fehler-“ >&2 ; echo „Ausgabe auf Display“

Auf dem Display lassen sich die beiden Ausgabekanäle nicht wirklich unterscheiden.
Schreibe nun die letzte Zeile in ein Shell-Skript ~/.local/bin/test.sh

#!/bin/sh
echo -n „Fehler-“ >&2 ; echo „Ausgabe auf Display“

Teste das Programm auf verschiedene Arten und prüfe Ausgabe auf Display und in Datei:

test.sh
test.sh > ~/test.txt
test.sh 2> ~/test.txt
test.sh 2> ~/test.txt » test.txt

7.9 Erneuter Programmstart ermöglichen – Laufzeitdaten

Laufzeitdaten sind Daten, die während des Programmablaufs entstehen, und in Variablen gespeichert sind. Bei einem unerwarteten Programmabsturz gehen diese Daten verloren, damit ist der Programm-Fortschritt nicht mehr eindeutig. Ein erneuter Programmstart ist u. U. mit Problemen verbunden.

Der geeignete Ablageort für Laufzeitdaten richtet sich nach der vorgesehenen Gültigkeit der Daten.

Bis zum erfolgreichen Programmablauf gültige temporäre Daten, die nicht den Rechner-Neustart überleben müssen, lassen sich von allen Benutzern unter /tmp ablegen.
Bis zum erfolgreichen Programmablauf gültige temporäre Daten, die auch den Rechner-Neustart überleben sollen, lassen sich von allen Benutzern unter /var/tmp ablegen.

Root:

Root kann auf unbestimmte Zeit gültige, auch von anderen Programmen nutzbare, Laufzeitdaten unter /var/local/lib ablegen.

Andere Benutzer:

Benutzer können bis zur Abmeldung (Logout) kurzfristig gültige Daten unter /run/user/$(id -u) ablegen.
Benutzer können auf unbestimmte Zeit gültige, auch von anderen Programmen nutzbare, Laufzeitdaten unter $HOME/.cache/local/lib ablegen.

7.10 Fortschrittsmeldungen protokollieren – Log-Datei

Fortschrittsmeldungen enthalten Informationen zum Programmfortschritt, und sollten in eine Datei protokolliert werden.

Root:

Root kann Log-Dateien unter /var/local/log ablegen.

Andere Benutzer:

Benutzer können Log-Dateien unter $HOME/.cache/local/log ablegen.

7.11 Parallele Prozesse synchronisieren – wait, kill, trap

7.11.1 wait-Kommando

Um 2 oder mehr Prozesse einander zu synchronisieren können Lock-Dateien (siehe 7.4) eingesetzt werden. Diese enthalten die für das wait-Kommando benötigte Prozess-ID des parallelen Prozesses.

if [ -r “/var/local/lock/programmname.lock“ ]; then
ppid=$(echo $(cat test.txt))
wait $ppid
fi

Das wait-Kommando wartet solange, bis der angegebene Prozess beendet ist.

Anderes Beispiel:

#!/bin/sh
# fluxbox startup-script:

# Deutsche Tastaturbelegung
xmodmap "/home/rudi/.Xmodmap"
setxkbmap -model evdev -layout de

# Fluxbox starten
exec fluxbox &
wmpid=$!

sleep 1

{

# Autostart-Programme
volumeicon &
nm-applet &
fdpowermon &

} &

# Haltepunkt. Programm darf erst enden, wenn Fluxbox beendet wurde
wait $wmpid

7.11.2 kill und trap

Das kill-Kommando kann Signale zu anderen Prozessen senden. Die Signale SIGUSR1 und SIGUSR2 haben die Nummern 10 und 12, und können frei verwendet werden.

if [ -r “/var/local/lock/programmname.lock“ ]; \then ppid=$(echo $(cat test.txt)) kill -10 $\ppid fi

Das angesprochene Programm kann mit dem trap-Kommando darauf reagieren:

while true ; \do trap break \10 done

7.11.3 Liste von Signale und deren Nummer

1SIGHUPWiederherstellung der Kontrolle, wenn Prozess sich aufgehängt hat
2SIGINTStrg-c
3SIGQUITStrg-d
4SIGILL
5SIGTRAP
6SIGABRT
7SIGBUS
8SIGFPESignal wenn eine unerwartete mathematische Operation ausgeführt wurde
9SIGKILLProzess sofort beenden
10SIGUSR1Benutzerdefiniertes Signal
11SIGSEGV
12SIGUSR2Benutzerdefiniertes Signal
13SIGPIPE
14SIGALRMSignal für Alarm
15SIGTERMProzess beenden
16SIGSTKFLT
17SIGCHLDProzess anhalten
18SIGCONT
19SIGSTOPProzess anhalten
20SIGTSTP
21SIGTTIN
22SIGTTOU
23SIGURGProzess anhalten
24SIGXCPU
25SIGXFSZ
26SIGVTALRM
27SIGPROF
28SIGWINCH
29SIGIO
30SIGPWR
31SIGSYS
34SIGRTMIN
35SIGRTMIN+1
36SIGRTMIN+2
37SIGRTMIN+3
38SIGRTMIN+4
39SIGRTMIN+5
40SIGRTMIN+6
41SIGRTMIN+7
42SIGRTMIN+8
43SIGRTMIN+9
44SIGRTMIN+10
45SIGRTMIN+11
46SIGRTMIN+12
47SIGRTMIN+13
48SIGRTMIN+14
49SIGRTMIN+15
50SIGRTMAX-14
51SIGRTMAX-13
52SIGRTMAX-12
53SIGRTMAX-11
54SIGRTMAX-10
55SIGRTMAX-9
56SIGRTMAX-8
57SIGRTMAX-7
58SIGRTMAX-6
59SIGRTMAX-5
60SIGRTMAX-4
61SIGRTMAX-3
62SIGRTMAX-2
63SIGRTMAX-1
64SIGRTMAX

7.12 Programm beenden – Exit-Status

Wenn ein Programm erfolgreich endet, liefert es den Exit-Status 0 (Voreinstellung), ansonsten einen Wert > 0.

exit
exit n

Das gleiche gilt für die Shell-Funktion (Rückgabewert), denn die Shell-Funktion ist (nichts weiter als) ein Shell-Programm eingebettet in ein Shell-Programm.

Der Exit-Status kann mit $? abgefragt werden

echo “$?“

Intern ist der Exit-Status ein 8-Bit-Wert. Damit können je Programm bis zu 255 verschiedene Fehlermeldungen frei zugewiesen werden.

7.12.1 Liste häufig verwendeter Exit-Codes

0Success 68Advertise error
1Operation not permitted 69Srmount error
2No such file or directory 70Communication error on send
3No such process 71Protocol error
4Interrupted system call 72Multihop attempted
5Input/output error 73RFS specific error
6No such device or address 74Bad message
7Argument list too long 75Value too large for defined data type
8Exec format error 76Name not unique on network
9Bad file descriptor 77File descriptor in bad state
10No child processes 78Remote address changed
11Resource temporarily unavailable 79Can not access a needed shared library
12Cannot allocate memory 80Accessing a corrupted shared library
13Permission denied 81.lib section in a.out corrupted
14Bad address 82Attempting to link in too many shared libraries
15Block device required 83Cannot exec a shared library directly
16Device or resource busy 84Invalid or incomplete multibyte or wide character
17File exists 85Interrupted system call should be restarted
18Invalid cross-device link 86Streams pipe error
19No such device 87Too many users
20Not a directory 88Socket operation on non-socket
21Is a directory 89Destination address required
22Invalid argument 90Message too long
23Too many open files in system 91Protocol wrong type for socket
24Too many open files 92Protocol not available
25Inappropriate ioctl for device 93Protocol not supported
26Text file busy 94Socket type not supported
27File too large 95Operation not supported
28No space left on device 96Protocol family not supported
29Illegal seek 97Address family not supported by protocol
30Read-only file system 98Address already in use
31Too many links 99Cannot assign requested address
32Broken pipe 100Network is down
33Numerical argument out of domain 101Network is unreachable
34Numerical result out of range 102Network dropped connection on reset
35Resource deadlock avoided 103Software caused connection abort
36File name too long 104Connection reset by peer
37No locks available 105No buffer space available
38Function not implemented 106Transport endpoint is already connected
39Directory not empty 107Transport endpoint is not connected
40Too many levels of symbolic links 108Cannot send after transport endpoint shutdown
42No message of desired type 109Too many references
43Identifier removed 110Connection timed out
44Channel number out of range 111Connection refused
45Level 2 not synchronized 112Host is down
46Level 3 halted 113No route to host
47Level 3 reset 114Operation already in progress
48Link number out of range 115Operation now in progress
49Protocol driver not attached 116Stale file handle
50No CSI structure available 117Structure needs cleaning
51Level 2 halted 118Not a XENIX named type file
52Invalid exchange 119No XENIX semaphores available
53Invalid request descriptor 120Is a named type file
54Exchange full 121Remote I/O error
55No anode 122Disk quota exceeded
56Invalid request code 123No medium found
57Invalid slot 125Operation canceled
59Bad font file format 126Required key not available
60Device not a stream 127Key has expired
61No data available 128Key has been revoked
62Timer expired 129Key was rejected by service
63Out of streams resources 130Owner died
64Machine is not on the network 131State not recoverable
65Package not installed 132Operation not possible due to RF-kill
66Object is remote 133Memory page has hardware error
67Link has been severed

8. Unix-Textverarbeitung

col umgekehrte Zeilenvorschübe aus der Eingabe filtern
comm Zwei sortierte Dateien Zeile für Zeile vergleichen
cut Teile jeder Zeile aus Dateien entfernen
diff Dateien zeilenweise vergleichen
grep gibt Zeilen aus, die zu Suchmustern passen
egrep
fgrep
expand Tabulatoren in Leerzeichen umwandeln
fold jede Eingabezeile so umbrechen, dass sie in eine angegebene Breite passt
gettext kann Nachrichten übersetzen
head den ersten Teil von Dateien ausgeben
iconv Zeichenkodierung eines Texts in eine andere umwandeln
join Zeilen von zwei Dateien über ein gemeinsames Feld verbinden
nl Zeilen von Dateien nummerieren
od Dateien im Oktal- und anderen Formaten ausgeben
paste Zeilen von Dateien zusammenfügen
printf Daten formatieren und ausgeben
seq eine Zahlenfolge ausgeben
sort Zeilen von Textdateien sortieren
tail Den letzten Teil von Dateien ausgeben
tee Von Standardeingabe lesen und in Standardausgabe und Dateien schreiben
tr Zeichen umwandeln oder löschen
tsort topologisch sortieren
unexpand Leerzeichen in Tabulatoren umwandeln
uniq doppelte Zeilen berichten oder entfernen
wc Anzahl der Zeilen, Wörter und Byte für jede Datei ausgeben

9. Programmierwerkzeuge

9.1 Rechnersprache bc

Die POSIX Standard-Shell kann nur Zeichenketten numerisch vergleichen (>, <, =, …), kennt jedoch keinerlei numerische Berechnungen. Selbst für das einfache hoch oder runter zählen von Zahlen z. B. in Programmschleifen werden externe Programme wie bc oder awk als Unix-Werkzeuge benötigt.

bc (binary calculator) ist eine interaktive Algebra–Sprache mit nahezu beliebiger Festkomma-Genauigkeit.

Hier wird die POSIX Standard-Version von bc vorgestellt.

bc unterstützt:

unterschiedliche Zahlenbasis für Eingabe (2 … 16) und Ausgabe (2 … 999)

je 26 Namen (a … z) für Variablen, eindimensionale Arrays, Funktionen

Grundrechenarten, Zuweisungen +, -, *, /, %, ^, sqrt(), =, +=, -=, *=, /=, %=, ^=

Postinkrement/-dekrement, Preinkrement/-dekrement (++, –)

Vergleiche ( <, >, ==, !=, ⇐, >=, == )

optionale Funktionen sin s(x), cos c(x), arctan a(x), ln l(x), ex e(x), Bessel-Funktion j(n,x)

Programmablaufsteuerung if, while, for, break, return, quit, /* … */

Ausgabe zusätzlicher Informationen “string“

Beispiel: Berechnung und Zuweisung von π. (Hinweis: arctan( 1 ) = 45° oder π / 4)
pi=$(echo „scale=10; 4 * a( 1 )“ | bc -l)


9.2 Datum/Uhrzeit date

date zeigt / ändert die Systemzeit, und unterstützt dabei Zeitzonen und die Umrechnung nach Unix-Zeit (Anzahl Sekunden seit 1.1.1970 00:00) und zurück.

Das Programm date enthält auch eine Mini-Programmiersprache für das Rechnen mit Terminen.

Beispiele:

Wie lautet die jetzt aktuelle Unix-Zeit?
date +%s

Wie lautet die Unix-Zeit um 04:35 Uhr am nächsten Samstag?
date –date='04:35 next Sat' +%s

9.3 Stream–Editor Sed

sed ist ein Stream-Editor für die grundlegende Texttransformationen auf einen Eingabestrom (einer Datei oder aus einer Verarbeitungskette). Sed kann z. B. Text anhängen, einfügen, löschen, suchen und ersetzen. Gesucht wird mittels regulären Ausdrücken.

Mehrere Sed-Kommandos können aus einem Programmskript eingelesen werden. Dabei werden auch Kommentarzeilen, Label, unbedingte und bedingte Sprünge (falls eine Textstelle ersetzt werden konnte) unterstützt.

Beispiele:

# lösche in einer Datei die 4. Zeile
$ sed '4d' input.txt > output.txt

# ersetze in den Zeilen 10-20 jedes Vorkommen von 'hello' durch 'world'
$ sed '10,20s/hello/world/' input.txt > output.txt

9.4 Interpreter für AWK

mawk ist ein Interpreter für die Programmiersprache AWK, benannt nach den Anfangsbuchstaben der Entwickler (Aho, Weinberger und Kernigham). Die Sprache AWK ist nützlich zur Manipulation von Daten, für die Texterstellung und -verarbeitung und für Prototypen und Experimente mit Algorithmen.

AWK wertet Eingaben im Prinzip immer als eine Tabelle aus, welche sich in Zeilen und Spalten zerlegen lässt. Die Trennzeichen für Zeilen und Spalten können frei gewählt werden.

Wie arbeitet AWK? - Programmiermodell

Ein AWK-Programm besteht aus 4 Teile: BEGIN-Teil, Bedingungen, Verarbeitungsteil und END-Teil.

Alle 4 Teile sind optional, es muss lediglich wenigstens ein Teil vertreten sein.

1. BEGIN { Anweisungen }
Dieser Teil wird als erstes ausgeführt, hier stehen Initialisierungen, Funktionsdefinitionen, …

2. Einlesen der nächsten Eingabezeile, zerlegen in Spalten gemäß Trennzeichen, und ablegen in die
Variablen $1 … $9. In $0 steht die gesamte Eingabezeile.

3. Bedingung1 { Anweisungen }
Bedingung2 { Anweisungen }


Verarbeitungsteil. Wenn eine (optionale) Bedingung auf die eingelesenen Daten zutrifft, wird der dazugehörige Anweisungsblock ausgeführt.
Dieser Programmteil wird nach jeder Eingabezeile erneut ausgeführt.

4. Noch eine Eingabezeile vorhanden? Wenn ja dann weiter bei 2., wenn nein dann …

5. END { Anweisungen }
Dieser Teil wird zuletzt ausgeführt, und dient meistens der Ausgabe der berechneten Ergebnisse.

AWK–Programme haben eine C–ähnliche Syntax und sind auf Grund ihrer besonderen Datenverarbeitungs-Struktur meist deutlich kürzer als in anderen Programmiersprachen. Die Sprache AWK unterstützt:

2 grundlegende Datentypen: Numerisch und Zeichenkette, sowie Arrays, autom. Typzuweisung

Verzweigungen: if …, if … else …

Programmschleifen: while …, do … while …, for …, continue, break

benutzerdefinierte Funktionen: function name() { Anweisungen; return }

Regulärer Ausdruck als Bedingung: / regex /

Aus C bekannte Zuweisungen und Operatoren

String-Funktionen

Funktionen für Datum und Zeit

Mathematische Funktionen: sin(), cos(), atan2(), log(), exp(), int(), rand(), sqrt()

Weitere Eingabekanäle z. B. aus Dateien

(Formatierte) Ausgabe nach stdout, stderr, in Datei

Zugriff auf Shell-Umgebungsvariablen

Zugriff auf Kommandozeilenparameter des AWK-Interpreters

Ausführung von Kommandos in Sub-Shell

Beispiel für ein kurzes AWK-Programm:

Aufgabe: Welche Nummer 1 … 6 hat das aktuelle Terminal?

Lösung: Das Kommando tty liefert /dev/tty1 … /dev/tty6. Links von der gewünschten Ziffer steht der Buchstabe ‚y‘, der nur einmal vorkommt. Mit ‚y‘ als Trennzeichen ergibt sich eine „Tabelle“ mit 1 Zeile und 2 Spalten. Die 1. Spalte enthält immer „/dev/tt“, die 2. Spalte soll ausgegeben werden.

TTYnr=$(tty | awk -F „y“ – '{ print($2) }')

9.5 Mathematische Konstanten und abgeleitete Formeln

gedacht für die Anwendung in bc oder awk.

Konstanten:
π = 3.141592653589793238462643383279502884197169399375105820974944592
e = 2.718281828459045235360287471352662497757247093699959574966967627
π = 4 * arctan 1
e = e 1

Logarithmische und Exponential-Funktionen
log b x = ln x / ln b
lg x = ln x / ln 10
y x = e x * ln y
10 x = 10 ^ x = e x * ln 10

Trigonometrische und Arcus-Funktionen (in Radiant)
tan x = sin x / cos x
cot x = cos x / sin x
arcsin x = arctan(x / sqr(1 – x 2 ) )
arccos x = π / 2 - arctan(x / sqr(1 – x 2 ) )
arccot x = 1 / arctan x
x (in Grad) = x (in Radiant) * 180 / π

Polare (r, φ) und rechtwinklige (x, y) Koordinaten
r = srq(x 2 + y 2 )
φ = arctan(y / x)
x = r * cos φ
y = r * sin φ

Hyperbolische und Area-Funktionen
sinh x = (e x – e -x ) / 2
cosh x = (e x + e -x ) / 2
tanh x = (e x – e -x ) / (e x + e -x )
coth x = (e x + e -x ) / (e x – e -x )
arsinh x = ln(x + sqr(x 2 +1) )
arcosh x = ln(x + sqr(x 2 – 1) )
artanh x = ln( (1 + x) / (1 - x) ) / 2
arcoth x = ln( (x + 1) / (x - 1) ) / 2


9.6 Makroprozessor m4

m4 ist eine Implementierung des traditionellen UNIX–Makroprozessors.

Ein Makroprozessor enthält eine Mini-Programmiersprache für Textersetzungen anhand gegebener Definitionen. Dabei werden die Eingabedaten entsprechend erweitert / verändert zur Ausgabe durchgeleitet (Vergleiche auch: C Präprozessor).

M4 unterstützt

Makro-Definitionen, mit Parameter,

umbenennen, entfernen, neu definieren von Makros

Mathematische Grundrechenarten, Bitweise Logik, Vergleichs- und logische Operatoren

Verzweigungen: ifdef, ifelse

Programmschleifen: forloop, foreach

Rekursion: shift

Arbeiten mit Datenablage auf Stack

Definieren von berechneten Makros mit Makros: composite

String-Funktionen, Suchen mit regulärem Ausdruck, String formatieren

Öffnen weiterer Kanäle für Eingabedaten aus Dateien

Ausführung von Kommandos in Sub-Shell und Nutzung deren Ausgabe als Eingabedaten

und stellt damit eine vollständige Programmiersprache für jedes noch so komplexe Problem bereit.

Beispiel: Mit bc Flächeninhalt eines Kreises A = r 2 * π berechnen

Eigener Programmtext in kreis.src:

Flaeche = POT(2,Radius)*PI

Makrodefinitionen für m4 in bc.m4. ‚$1‘ und ‚$2‘ sind hier Makroparameter.

define(POT, (e($1*l($2))))
define(Flaeche, a[1])
define(Radius, r)
define(PI, 3.1416)

Mit m4 Programmtext für bc nach kreis.bc übersetzen

m4 bc.m4 kreis.src > kreis.bc⇒ Inhalt von kreis.bc

a[1] = (e(2*l(r ))*3.1416

9.7 C Entwicklungsumgebung

Programmierung in Assembler

Die 2. Generation der Programmiersprachen stellen die Assemblersprachen dar. Hierbei werden den binären oder hexadezimalen Maschinencodes leichter merkbare Kürzel, so genannte „Mnemonics“, zugeordnet, welche vom Assembler in Maschinencode übersetzt werden.
Zumindest ein Prozessorspezifischer Assembler wird üblicherweise vom Chip-Hersteller angeboten. Er unterstützt den gesamten Maschinenbefehlssatz einschließlich aller Adressierungsarten.

Heutzutage sind es meistens symbolische Makro-Assembler. Sie unterstützen:

Aufteilen des Quelltextes auf mehrere Dateien

Einfügen von Kommentaren

Übersichtliche Unterteilung in Programmsegment und Datensegment

Konstante Werte direkt als Binärzahl, Oktalzahl, Dezimalzahl, Hexadezimalzahl, Zeichen oder Zeichenkette angegeben, und symbolisch benannt

Konstante Werte als zu berechnenden, arithmetischen Ausdruck angegeben

Speicheradressen symbolisch benannt

Rekursiv definierbare Makros mit Parameterübergabe

Bedingtes Assemblieren

Direktiven für die übersichtliche Behandlung besonderer Programmsituationen (Interrupt)

Programmbibliothek für häufige Programmieraufgaben

All diese Merkmale zusammen machen das Programm nicht nur deutlich lesbarer, sondern lassen die Assemblersprache fast wie eine Hochsprache erscheinen.

Programmiersprache C

C gehört zu den problemorientierten bzw. imperativen Programmiersprachen der 3. Generation. Charakteristisches Merkmal ist die algorithmische Herangehensweise an Probleme. Da die imperativen Sprachen zu den höheren Programmiersprachen gehören, müssen sie vor der Ausführung durch einen Compiler in Maschinencode umgewandelt werden.

Das zu lösende Programmierproblem wird vom Programmierer in Algorithmen (Lösungsrezepte) zerlegt. Der einzelne Algorithmus wiederum setzt sich zusammen aus Konstanten, vordefinierte und selbst definierbare abstrakte Datentypen, Variablen, Zuweisungen, Programmverzweigungen, Programmschleifen, sowie vordefinierte und selbst definierbare Funktionen.

Ein typisches C–Entwicklungssystem setzt sich zusammen aus Texteditor, C–Präprozessor, C–Compiler, Assembler und Linker.

Mit dem Texteditor wird das C–Programm erstellt. Ein C–Programm kann sich aus ein oder mehreren Quelltextdateien zusammensetzen, von denen jede einen Teil des C–Programms enthält.

Der C–Präprozessor ist ein einfacher Makroprozessor, der den Quelltext des C–Programms bearbeitet und für den C–Compiler verständlich macht. Er ermöglicht das Definieren von Konstanten und Textmakros, Aufteilung des Quelltextes auf mehrere Dateien, bedingte Kompilierung, und wird durch besondere Präprozessorbefehlszeilen gesteuert, die mit dem Zeichen '#' beginnen.

Der C–Präprozessor als separates Programm liest den vom Programmierer erstellten Quelltext ein und liefert als Ausgabe einen modifizierten, für den C–Compiler leichter verständlichen Quelltext ohne Präprozessoranweisungen.

Der C–Compiler übersetzt das C–Programm in für den Linker verständlichen Objektcode.

Der Assembler übersetzt den Prozessorspezifischen Assemblercode in für den Linker verständlichen Objektcode.

Der Linker schließlich hat die Aufgabe, die Objektmodule von C–Compiler und Assembler mit den Laufzeitbibliotheken zu binden, und so alles zu einem lauffähigen Programm zusammen zu setzen.

make GNU-Make-Dienstprogramm zur Verwaltung von Programmgruppen

Die folgenden Programme können natürlich auch direkt aufgerufen werden.

cpp Der C Präprozessor
gcc GNU C und C++ Compiler
ld Der GNU linker
strip entfernt Symbole und andere Daten von Objekt-Dateien

9.8 Zusammenfassung der empfohlenen Ablageorte

Von Root installierte Drittanbieter-Programme:

/etc/optsiehe /opt/etc
/opt/<Paketname>/Verzeichnis für Programm-Paket. Enthaltene Programme können nach /usr/local/bin oder /usr/local/sbin verlinkt sein.
/opt/binProgramme für alle Benutzer. Enthaltene Programme können nach /usr/local/bin verlinkt sein.
/opt/docProgramm-Dokumentation
/opt/etcKonfigurationsdateien für Programme in /opt. /etc/opt sollte Link auf /opt/etc sein, oder umgekehrt.
/opt/includeC Header-Dateien
/opt/libObjektdateien und andere Bibliotheken
/opt/sbinSystem-Programme. Enthaltene Programme können nach /usr/local/sbin verlinkt sein.
/opt/srcC Quelltexte
/var/optLaufzeitdaten für Programme in /opt

Programme von Root:

/etc/localsiehe /usr/local/etc
/usr/local/binProgramme für alle Benutzer. Darf keine Unterverzeichnisse enthalten.
/usr/local/docProgramm-Dokumentation

/usr/local/etc
Konfigurationsdateien für Programme in /usr/local/bin und /usr/local/sbin. /etc/local sollte Link auf /usr/local/etc sein, oder umgekehrt.
/usr/local/includeHeader-Dateien für Compiler-Sprachen (z. B. C)
/usr/local/libObjektdateien für Compiler-Sprachen (z. B. C), sowie Programmtexte die nicht direkt aufgerufen werden (z. B. bc, awk, sed, m4)
/usr/local/sbinSystem-Programme. Darf keine Unterverzeichnisse enthalten.
/usr/local/srcQuelltexte für Compiler-Sprachen (z. B. C)
/var/localLaufzeitdaten für Programme in /usr/local/bin und /usr/local/sbin.

Programme von Benutzern:

$HOME/.cache/localLaufzeitdaten für Programme in $HOME/.local/bin.
$HOME/.config/localKonfigurationsdateien für Programme in $HOME/.local/bin.
$HOME/.local/binProgramme für lokalen Benutzer. Sollte keine Unterverzeichnisse enthalten.
$HOME/.local/docProgramm-Dokumentation
$HOME/.local/includeHeader-Dateien für Compiler-Sprachen (z. B. C)
$HOME/.local/libObjektdateien für Compiler-Sprachen (z. B. C), sowie Programmtexte die nicht direkt aufgerufen werden (z. B. bc, awk, sed, m4)
$HOME/.local/srcQuelltexte für Compiler-Sprachen (z. B. C)

10. Automatisierung mit System-Dienste

10.1 Programm zeitversetzt ausführen – at

Mit dem System-Dienst at lassen sich Programme zeitversetzt ausführen.

Beispiel: Termin-Erinnerung am 5. Januar um 19:35 Uhr

echo 'echo Termin-Erinnerung' | at -t 01051935

Hier wird das auszuführende Kommando per Standard-Eingabe übergeben. Die Ausgabe erfolgt übrigends nicht auf dem Display (wie vielleicht erwartet), sondern wird per lokaler Mail versendet. Alternativ können Ausgaben auch in eine Datei umgeleitet werden.

10.2 Programm regelmäßig ausführen – cron

Mit dem System-Dienst cron lassen sich Programme regelmäßig ausführen. Programme werden genau zum angegebenen Zeitpunkt gestartet. Cron lässt sich am besten auf Rechner-Systeme einsetzen, die 24 Stunden rund um die Uhr laufen (z. B. Server).

crontab -l listet die aktuelle Cron-Tabelle auf

crontab -e Cron-Tabelle editieren und aktualisieren
1. Spalte: Minute (0-59)
2. Spalte: Stunde (0-23)
3. Spalte: Tag (1-31)
4. Spalte: Monat (1-12, oder jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec)
5. Spalte: Wochentag (0-7, oder sun, mon, tue, wed, thu, fri, sat), sowohl 0 als auch 7 stehen für Sonntag
6. Spalte: Kommando mit optionale Parameter

In der System-Cron-Tabelle für Root steht in der 6. Spalte der Benutzername und in der 7. Spalte Kommando mit optionalen Parameter.

* steht für beliebigen Eintrag,
*/2 steht hier für jede zweite (Minute, oder Stunde …)
1,2,5,9 gültige Werte in einer Liste
0-4,8-12 gültige Werte als Bereichsangabe

Spezielle Zeitangaben:

@reboot Startet Programm einmalig nach Rechner-Neustart
@yearly Startet Programm einmal im Jahr, entspricht „0 0 1 1 *“
@annually Startet Programm einmal im Jahr, entspricht „0 0 1 1 *“
@monthly Startet Programm einmal im Monat, entspricht „0 0 1 * *“
@weekly Startet Programm einmal pro Woche, entspricht „0 0 * * 0“
@daily Startet Programm einmal am Tag, entspricht „0 0 * * *“
@hourly Startet Programm einmal pro Stunde, entspricht „0 * * * *“

Ausgaben erfolgen übrigends auch hier nicht auf dem Display, sondern werden per lokaler Mail versendet. Alternativ können Ausgaben auch in eine Datei umgeleitet werden.

10.3 Nachrichten senden und empfangen – mail

Displayausgaben lassen sich nur dann betrachten, wenn man gerade angemeldet ist und auf das Display schaut. Bei zeitversetzten Prozessen wie bei at und cron kann man sich nicht darauf verlassen. Daher werden Standard-Ausgaben und Fehlermeldungen standardmäßig per Mail versendet. So kann der Empfänger selbst bestimmen wann er zu lesen bereit ist.

Auch das schreiben von Meldungen in eine Datei ist nicht immer die beste Lösung, insbesondere wenn mehr als ein Benutzer im System eingerichtet ist. Je nachdem welcher Benutzer das Kommando per at oder cron aufruft stimmen möglicherweise die Zugriffsrechte nicht … und resultierende Fehlermeldungen werden dann doch per Mail versendet.

Das Programm mail ist sehr komplex, hier werden nur die wichtigsten Kommandos vorgestellt.

10.3.1 Nachrichten senden

Nachrichten können ganz leicht lokal an sich selbst oder an andere eingerichtete Benutzer versendet werden.

Beispiel:

echo „Dies ist ein Test.“ | mail -s „Test-Nachricht“ anton

sendet an den Benutzer anton eine Mail mit dem Titel „Test-Nachricht“ und dem Inhalt „Dies ist ein Test.“.

10.3.2 Empfangene Nachrichten lesen

mail ruft das Programm mail interaktiv auf und zeigt die Köpfe ungelesener Nachrichten an
? t (type) zeigt die erste Nachricht an
? d (delete) löscht die erste Nachricht
? t (type) zeigt die nächste Nachricht an …
? d (delete) löscht die nächste Nachricht …
? q (quit) verlässt das Mail-Programm

Seiten