Druckfassung
Die Shell ist eine eigenständige Programmiersprache. Sie kennt Variablen und
die meisten Kontrollstrukturen einer höheren Programmiersprache.
- Ein Shellscript ist eine Textdatei, die Befehle für die
Shell enthält. Um ein Shellscript zu erstellen, gibt man mit einem Editor
(z.B. dem vi) eine Befehlsfolge ein und speichert
diese in einer Datei ab.
- Befehle können sich über mehrere Zeilen erstrecken. Mehrere Befehle in
einer Zeile werden durch das Zeichen ; getrennt.
- Mit dem Befehl
-
- chmod +x dateiname
teilt man dem System mit, daß es
sich bei dateiname um eine ausführbare Datei handelt.
- Jede der folgenden Eingabezeilen
-
./dateiname
dateiname # aktuelles Verzeichnis im Suchpfad der Shell ?
/bin/bash dateiname
startet das in der Datei dateiname abgelegte Script
in einer eigenen Subshell; der Aufruf (nur innerhalb der Bourne-Shell und
Verwandten)
-
- . dateiname
führt das Script in der gleichen Shell aus
(d.h. Werte von Umgebungsvariablen, die mit Hilfe des Scriptes gesetzt werden,
sind in der aufrufenden Shell anschließend verfügbar).
- Um Verwechslungen zu vermeiden, wird in der ersten Zeile von Shellscripten
durch einen speziellen Kommentar vermerkt, daß der folgende Inhalt durch die
Bash /bin/bash abzuarbeiten ist. Allgemein steht in
dieser shebang-Zeile der Name der gewünschten Shell.
Beispiel:
-
#!/bin/bash
# Kommentare werden durch dieses Zeichen kenntlich gemacht
echo Dateiverzeichnis von \
`pwd`: # Fortsetzungszeile
ls -la
In der Regel lesen Kommandos ihre
Eingabe von der Standard-Eingabe (stdin, Kanal 0, normalerweise
die Tastatur) und schreiben ihre Ausgabe auf die
Standard-Ausgabe (stdout, Kanal 1, normalerweise der
Bildschirm). Fehlermeldungen erfolgen auf die Fehlerausgabe
(stderr, Kanal 2, ebenfalls der Bildschirm). Soll die Ein/Ausgabe von bzw. auf
normale Dateien erfolgen, kann die Ein- bzw. Ausgabe umgelenkt
werden.
- Eingabe umlenken:
-
- kommando < eingabedatei
Wirkung:
kommando liest seine Eingabe von der Datei eingabedatei
- Here-Dokumente sind ein Sonderfall der Eingabeumlenkung,
bei dem die Eingabe im Skript selbst mit abgelegt ist.
-
kommando << bezeichner
eingabezeile 1
...
bezeichner
Wirkung: Alle Zeilen zwischen der Kommandozeile selbst und der
Zeile mit dem bezeichner (ab der ersten Spalte) sind Eingaben für
kommando.
- Ausgabe umlenken:
-
- kommando > ausgabedatei
Wirkung:
kommando schreibt seine Ausgaben in die Datei
ausgabedatei.
-
- kommando >> ausgabedatei
Wirkung: kommando fügt seine Ausgaben an das Ende der
Datei ausgabedatei an.
- Die Fehlerausgabe läßt sich analog der Ausgabe umlenken,
wenn statt > oder >> die Metazeichen
2> bzw. 2>>
benutzt werden. Mit dem Zeichen 2>&1 werden
Fehlermeldungen in die gleiche Datei wie die Ausgabe geleitet.
Beispiel:
-
- sh -v script > ausgabe 2> trace_und_fehler
- Will man innerhalb des Scriptes die Fehlerausgabe verwenden, um z.B. eine
Fehlermeldung abzusetzen, so schreibt man in &2
hinein.
Beispiel:
-
- echo Fehlerhaftes Argument >&2
- Variablennamen beginnen mit einem Buchstaben und können
Ziffern sowie _ enthalten; nach einer Konvention sollte man für Shellvariablen
ausschließlich Großbuchstaben verwenden. Namen für Shellvariablen dürfen aus
bis zu 14 Zeichen bestehen.
- Die Wertzuweisung erfolgt durch
-
- NAME=wert
dabei dürfen zwischen NAME, = und
wert keine Leerzeichen stehen.
- Der Wert (Inhalt) einer Shellvariablen ist
-
- ${NAME}
- oder
- $NAME
Die erste Variante ist geboten, wenn z.B. direkt
hinter der Variablen noch weiterer Text folgt. Werte von Shellvariablen sind
stets Zeichenketten. Nicht existente Variablen werden als leer angesehen.
- Shellvariablen sind nur innerhalb der ausführenden Shell gültig; sollen
sie an Kindprozesse (Subshells) mitgegeben werden, muß die Variable mit dem
export Kommando entsprechend gekennzeichnet werden:
-
- export NAME
- Die Übergabe einer Shellvariablen bzw. deren Wert von einem Kindprozeß an
den Vaterprozeß ist nicht möglich.
- Der Wert einer Variablen kann jederzeit geändert werden. Man löscht eine
Variable mit
-
- unset NAME
- Variablen sind Bestandteile der Shell-Umgebung
(Environment) und können mit
-
- set
angezeigt werden.
- Shellvariablen können von der Standard-Eingabe eingelesen
werden:
-
- read NAME1 NAME2 ...
- Beispiel:
Script readit:
-
#!/bin/bash
read VAR1 VAR2 REST
echo $VAR2
echo $REST
Aufruf von readit:
-
chmod +x readit
readit << +eoi
Dies ist die Eingabe des Beispiels
+eoi
Ausgabe:
-
ist
die Eingabe des Beispiels
Neben der einfachen
Einsetzung von Variablenwerten durch $NAME kennt die
Bash Varianten, die die Ersetzung von Bedingungen abhängig machen. Hierbei geht
es oft darum, was zu tun ist, wenn die Variable leer ist oder nicht existiert.
- ${variable:-value}
- Default-Wert festlegen. Falls die Variable leer ist oder
nicht existiert, setze value ein, ansonsten ihren Wert.
- ${variable:=value}
- Default-Wert zuweisen. Falls die Variable leer ist oder
nicht existiert, weise ihr value zu. Anschliessend setze ihren Wert
ein.
- ${variable:+value}
- Alternativ-Wert festlegen. Falls die Variable leer ist
oder nicht existiert, setze einen leeren String ein, ansonsten den
Alternativwert.
- ${variable:?value}
- Fehler erzeugen. Falls die Variable leer ist oder nicht
existiert, value als Fehlermeldung ausgeben, ansonsten den Inhalt der
Variablen einsetzen.
- ${variable#value}
- Falls der Variableninhalt mit value als Muster beginnt, setze den
Variablenwert ein, wobei der Teil, der auf das Muster passt, gelöscht wird.
Anderenfalls erfolgt keine Löschung beim Ersetzen.
- ${variable%value}
- Falls der Variableninhalt mit value als Muster endet, setze den
Variablenwert ein, wobei der Teil, der auf das Muster passt, gelöscht wird.
Anderenfalls erfolgt keine Löschung beim Ersetzen.
Bei Weglassen des
Doppelpunktes in den obigen Formen erfolgt nur Abfrage auf nicht existente
Variablen, leere Inhalte werden wie andere behandelt.
Beispiele:
bash$ blubb=
bash$ echo ${blubb:-quark}
quark
bash$ blubb=käse
bash$ echo $blubb
käse
bash$ echo ${blubb:-quark}
käse
bash$ echo ${blubb:+quark}
quark
bash$ blubb=blah.f
bash$ echo ${blubb%.f}
blah
bash$ echo ${blubb%.x}
blah.f
- Umgebungsvariablen (bzw. environment variables) werden
dem Benutzer vom System für seine Umgebung mitgeliefert. Jedes von der Shell
aufgerufene Kommando erbt die Umgebungsvariablen (wie alles, was zur
Prozessumgebung gehört). Sie sollten nur mit Bedacht verändert werden.
- $TERM
- Bezeichnung für das verwendete Terminal. Anhand dieser Variablen
orientieren sich viele Programme (z.B. der vi),
wie Ausgaben für alphanumerische Anzeigen aufzubereiten sind.
- $HOME Pfad innerhalb des Dateisystems, der das
Heimatverzeichnis des Benutzers enthält.
- $PATH
- Suchpfade für Kommandos (durch das Zeichen : getrennt Liste von
Verzeichnisnamen)
- $IFS Trennzeichen der Shell (trennen z.B. Befehle von
Parametern und Parameter untereinander)
- Variablen, die von der Shell zu Beginn der Ausführung
eines Scriptes (außer $!, $?) gesetzt werden:
- $0
- Name des Scriptes, das gerade ausgeführt wird
- $*
- vollständige Parameterliste des Aufrufes dieses Scriptes
- $#
- Anzahl der an das Script übergebenen positionalen Parameter
- $1 $2 ...
- Namen der ersten positionalen Parameter (falls vorhanden), d.h. diese
Variablen enthalten die übergebenen positionalen Parameter.
- $$
- Prozeßnummer der Shell, die dieses Script ausführt
- $!
- Prozeßnummer des letzten im Hintergrund ausgeführten Prozesses
- $?
- der Exitstatus (return code) des letzten ausgeführten Kommandos
- $-
- enthält die momentan gesetzten Shell-Flags (vgl. das set-Kommando)
Von der Shell
auszuführende Befehle können eingebaute Kommandos (wie z.B. cd : change
working directory) sein oder externe Kommandos, d.h. in Dateien
abgelegte Programme oder Scripte (-> Unix Befehle). Die Shell bestimmt die
Position der Dateien, die ein externes Kommando realisieren, anhand des
absoluten oder relativen Pfadnamens
-
- [Pfad/]name [ optionen ] [ parameter ]
Um die
umständliche Angabe des Pfadnamens häufig benutzter Befehle zu vermeiden, führt
die Shell eine Liste von Verzeichnissen, die die gebräuchlichsten Kommandos
enthalten. Diese Liste ist in der Umgebungsvariablen
-
- PATH
abgelegt. Wurde bei einem Befehl
kein Pfad angegeben, durchsucht die Shell die Verzeichnisse in der Liste nach
einer ausführbaren Datei mit dem Namen name. Verzeichnisse innerhalb
der Liste werden durch das Zeichen : getrennt.
Beispiel:
-
export PATH=/usr/ucb:/bin:/usr/bin:/usr/local/bin:.
echo $PATH
Achtung: Das jeweils aktuell eingestellte
Arbeitsverzeichnis ist nicht automatisch Bestandteil des Suchpfades. Es kann
jedoch, wie im Beispiel, durch Angabe des Verzeichnisses "." in den Suchpfad
aufgenommen werden. Zur Vermeidung unbeabsichtigter Überlagerung von
Systemprogrammen durch gleichnamige Programme im aktuellen Verzeichnis sollte
das aktuelle Verzeichnis, wenn überhaupt, nur als letztes durchsucht werden.
Das Kommando which vollzieht den gleichen
Suchvorgang anhand der Variablen PATH wie bei der Befehlsausführung und
liefert für einen Kommandonamen name den Namen des Verzeichnisses,
indem die Datei zu finden ist.
Aufruf:
-
- which name
which selbst ist für
die Bash ein externes Programm (im Gegensatz z.B. zum
tcsh-Kommandointerpreter) und muß deshalb selbst über den Suchpfad
erreichbar sein oder mit absoluter Pfadangabe aufgerufen werden.
which befindet sich im Verzeichnis /usr/bin.
Beispiel:
-
which which
Ausgabe: /usr/bin/which
Das Kommando type teilt uns mit, ob ein Kommado
eingebaut, ein alias, oder ein externes Programm ist.
Beispiel:
-
type which
Ausgabe: which is /usr/bin/which
- Im allgemeinen arbeitet die Shell still vor sich hin; nach Abschluß eines
Kommandos wird unabhängig vom Erfolg eines Kommandos das nächste ausgeführt.
- Fehlermeldungen sind selten, auch bei Syntaxfehlern.
- Mit dem set Kommando (set shell flags) läßt sich
das Verhalten der Shell im Fehlerfalle steuern; die wichtigsten Optionen sind:
- -e
- (error) brich im Fehlerfalle die Ausführung des Scriptes ab.
- -v
- (verbose) gib das auszuführende Kommando ohne Ersetzung von
Variablenwerten bzw. Dateinamen-Expansion aus.
- -x
- (execute) gib das auszuführende Kommando mit durchgeführten Ersetzungen
aus.
Mit + statt
- kann ein Schalter zurückgesetzt werden.
- Abhängig vom Erfolg der Ausführung hinterlassen Unix-Kommandos einen
Ausführungsstatus (Exitstatus bzw.
return code). Falls die Ausführung erfolgreich war, ist der Ausführungsstatus
0. Der Ausführungsstatus des letzten ausgeführten Kommandos ist in der Umgebungsvariablen
$? verfügbar.
- Es können Kommandos in Abhängigkeit des Erfolges
(Exitstatus) eines anderen Kommandos ausgeführt werden:
- &&
- Befehl ausführen, falls der vorhergehende Befehl erfolgreich war:
-
- befehl_1 && befehl_2 # befehl_2, falls befehl_1
erfolgreich
- ||
- Befehl ausführen, falls der vorhergehende Befehl NICHT erfolgreich war:
-
- befehl_1 || befehl_2 # befehl_2, falls befehl_1 nicht
erfolgreich
Erfolgreich sind Programme mit Exitstatus
0, andere gelten als nicht erfolgreich.
Beispiel:
-
#!/bin/bash
set -v
a.out || rm core
set +v
- Jede Scriptzeile wird in einzelne Kommandos zerlegt. Kommandos innerhalb
einer Zeile bzw. Pipe werden durch die Metazeichen zur Ein/Ausgabeumlenkung
sowie durch die Zeichen ; & ( ) | ^ getrennt.
Einsetzen von Variablenwerten
(z.B. ${NAME})
- Dateinamenexpansion: reguläre Ausdrücke (z.B. *.f)
außerhalb von Klammern " ... " bzw. ' ... ' werden durch passende Dateinamen
ersetzt.
Beispiel:
-
- echo Dateien "*.c": *.c
- Innerhalb einzelner Kommandos werden die Trennzeichen $IFS
interpretiert, um Kommandonamen von Parametern und Parameter untereinander zu
trennen.
- Kommandosubstitution: Kommandos in Back-Quotes ` ... `
(von links oben nach rechts unten) werden ausgeführt und deren Standard-Ausgabe
an die Stelle des Kommandos eingesetzt. Die Bash kennt daneben auch noch die
Form $( ... ).
Beispiel:
-
- echo `pwd`
- Durch das eval Kommando kann ein Kommando vor
der Ausführung nochmals interpretiert werden.
Beispiel:
-
#!/bin/bash
B=quoted; A='${B}'
echo Inhalt von A = ${A} = `eval echo ${A}`
Ausgabe: Inhalt von A = ${B} = quoted
Das Kommando
expr interpretiert Konstanten bzw. Werte von Variablen
als ganze Zahlen, die miteinander verknüpft werden können.
Aufruf:
-
- expr Loperant op Roperant
expr wertet den Ausdruck
-
- Loperant op Roperant
aus und liefert das Ergebnis als
Standard-Ausgabe. Der Operator op zur Verknüpfung des
linken Operanden Loperand mit dem rechten Operanden
Roperand steht für eine der folgenden Möglichkeiten:
-
-
- +
- Addition
- -
- Subtraktion
- *
- Multiplikation
- /
- ganzzahlige Division
- %
- Divisionsrest der ganzzahligen Division (Modulus)
Ausdrücke können mit runden Klammern ( ..
) zusammengefaßt werden. Einige der Symbole besitzen für die Shell
eine besondere Bedeutung, z.B. das Zeichen * zur Dateinamenexpansion oder (
.. ) zur Zusammenfassung von Kommandos. Sie müssen daher durch einen
Backslash \ oder durch Klammerung vor der Shell verborgen werden.
Da expr
seinerseits ein Kommando ist, bietet die Bash auch ein
eingebautes Analogon
$(( ausdruck ))
Beispiel:
-
#!/bin/bash
...
NPLUS1=`expr $N + 1`
NPLUSplus=$(($N+1))
Hierbei ist allerdings zu beachten, dass Zahldarstellungen, die mit einer
Null beginnen, als Oktalzahlen interpretiert werden und zu Fehlern führen, wenn
sich eine 8 oder 9 darin verirrt hat.
Das Kommando
test überprüft eine Bedingung und liefert den Exitstatus
0 (true), falls die Bedingung erfüllt ist. Es macht
keine Ein- oder Ausgabe.
Aufruf:
-
- test bedingung
oder
-
- [ bedingung ]
In der zweiten Aufrufform
ist [ ... ] kein Hinweis auf einen optionalen Parameter:
[ ist ein Synonym für test
Das Gegenstück ] schließt die Formulierung der
Bedingung ab und startet die Auswertung.
-
-
bedingung (Auswahl)
| wahr, falls |
| Eigenschaften von Dateien |
-f file
| file vorhanden und kein Verzeichnis ist |
-d file
| file vorhanden und ein Verzeichnis ist |
-s file
| file vorhanden und nicht leer ist |
-x file
| file vorhanden und ausführbar ist |
file1 -nt file2
| file1 neuer als file2 ist (Modifikationsdatum) |
file1 -ef file2
| file1 identisch mit file2 ist (Links) |
| Vergleichen von Zeichenketten |
str1 = str2
| die Zeichenketten str1 und str2 gleich sind |
str1 != str2
| die Zeichenketten str1 und str2 nicht gleich sind
|
-z str
| die Zeichenkette str leer ist |
| Vergleichen von ganzen Zahlen |
n1 -eq n2
| die Zahlen n1 und n2 gleich sind |
| weitere mögliche Operatoren sind:
- -ne
- ungleich
- -ge
- größer oder gleich
- -gt
- größer
- -le
- kleiner oder gleich
- -lt
- kleiner
|
Verknüpfung von Bedingungen
-
-
- !
- Negierung (not)
- -o
- oder (or)
- -a
- und (and)
Beispiel:
-
- test $S1 = $S2 -a $N1 -eq 0
-
- if kommandoliste
then kommandolisteT
else kommandolisteF
fi
Zunächst wird kommandoliste
(z.B. ein test)
ausgeführt. Falls der Exitstatus
gleich 0 (d.h. true) ist, wird kommandolisteT ausgeführt, sonst
kommandolisteF . Der else-Teil kann ggf. entfallen.
Beispiel:
-
- befehl_11 und befehl_12 ausführen;
falls befehl_12 den Exitstatus 0
liefert, befehl_2 ausführen, sonst befehl_3.
-
if befehl_11; befehl_12
then befehl_2
else befehl_3
fi
Beispiel:
-
- if [ -f file -a -w file ]; then cat >> file; fi
-
- for variable [
in wörterliste ]
do
-
- kommandoliste
done
kommandoliste wird für jedes Wort der wörterliste
einmal ausgeführt; die Variable variable enthält das jeweilige Wort der
wörterliste. Entfällt wörterliste, wird $*
angenommen.
Beispiel:
-
- set *.f; for F; do f77 -c ${F}; done
Beispiel:
-
#!/bin/bash
for DIR in /tmp /usr/tmp ${HOME}/tmp
do
- rm -rf ${DIR}/*
done
Beispiel:
-
#!/bin/bash
# Aufruf: mpost brief user1 user2 ...
# brief an Benutzer user1, user2, .. verschicken
BRIEF=$1; shift
# Jetzt sind nur noch user1, user2 ... positionale Parameter
for USER; do mail ${USER} < ${BRIEF}; done
while
kommandolisteB
do
kommandoliste
done
Zunächst
wird kommandolisteB ausgeführt. Liefert es den Exitstatus
0, wird kommandoliste abgearbeitet, dann wieder
kommandolisteB ausgeführt, der Exitstatus überprüft usw.
Dies wird solange wiederholt, bis kommandolisteB erstmalig
nicht den Exitstatus 0 liefert. Die Bearbeitung wird dann im Anschluß an
done fortgesetzt.
Beispiel:
set *.f
while [ $# -gt 0 ]; do f77 -c $1; shift; done
In einer
Schleife (while, for, until) können die Befehle continue und
break auftauchen.
continue bewirkt, dass die Bearbeitung
der Schleife mit dem aktuellen Wert der Schleifenvariablen abgebrochen und mit
dem nächsten Wert begonnen wird. Alle Kommandos in der Schleife, die dem
continue fölgen, gelangen für den aktuellen Wert nicht mehr zur
Ausführung.
break bewirkt das vollständige Verlassen der Schleife.
Die Verarbeitung wird mit dem ersten Kommando hinter dem Ende der Schleife
fortgesetzt.
case wert
in
muster1) kommandoliste1
;;
muster2) kommandoliste2
;;
...
musterN)
kommandolisteN
;;
esac
Die
Shell wertet die Zeichenkette wert sowie die angegebenen Muster
muster1 ... musterN aus.
- wert kann eine Konstante sein oder durch Kommandointerpretation
(z.B. Einsetzen von Variablenwerten oder Kommandosubstitution) ermittelt
werden.
- Die Muster können reguläre Ausdrücke (z.B.
[s,d,c,z].*.f ) sein und auch Shellvariablen
enthalten. Innerhalb der Muster können reguläre Ausdrücke durch das Zeichen
| (Bedeutung: oder) kombiniert werden.
- Die Kommandoliste hinter dem ersten passenden Muster (bis
zum nächsten ;; ) wird ausgeführt und danach die
Kontrollstruktur verlassen.
Beispiel:
-
#!/bin/bash
for w in * ; do
case ${w} in
*.f) f77 -c ${w};;
\*) echo Das Verzeichnis ist leer;;
esac
done
Die Namen aller Dateien des aktuellen Verzeichnisses werden
nacheinander der Variablen w zugewiesen. Für jede konkrete Ersetzung wird
überprüft, ob der Dateiname die Erweiterung .f besitzt (Muster
*.f in der case Kontrollstruktur über den Wert ${w}). Falls
ja, wird dieses Fortran-Programm mittels des F77-Compilers übersetzt.
Alle anderen Dateien werden ignoriert.
Als ein Sonderfall wird die Situation
behandelt, wenn das Verzeichnis leer ist: In diesem Falle
findet bei * keine Dateinamensubstitution statt, sondern das Zeichen * bleibt
erhalten. Diese Situation soll mit einem Muster innerhalb des case-Konstruktes
abgefangen werden. Da das Muster * aber auf alle Zeichenketten paßt, muß diese
Sonderbedeutung mit dem Zeichen \ aufgehoben werden. Das Muster \* paßt
nur auf das Zeichen *.
In der Bash lassen sich auch
Unterprogramme formulieren, die man wie eingebaute Funktionen benutzen kann.
function() {
kommandoliste
}
Genau wie ein Shellscript findet die Funktion ihre Argumente in den
positionalen Parametern $1, $2, .. wieder. Da eine Funktion
formal wie ein Programm/Script behandelt wird, hat sie auch einen Rückgabewert.
Mit dem Befehl return value wird die Funktion
beendet und der Rückgabewert erzeugt.
Beispiel:
action() {
STRING=$1
echo -n "$STRING "
shift
initlog $INITLOG_ARGS -c "$*" && success "$STRING" || failure "$STRING"
rc=$?
echo
return $rc
}
Beispiel:
confirm() {
echo -n "Start service $1 (Y)es/(N)o/(C)ontinue? [Y] "
read answer
case $answer in
y|Y|"")
return 0
;;
c|C)
return 2
;;
n|N)
return 1
;;
*)
confirm $1
return $?
;;
esac
}