|
Was ist ein Shell Script? |
|
Ein Shell Script ist eine Textdatei bestehend aus |
|
• Program- und Funktionsaufrufen |
|
• Shell internen Kontrollstrukturen |
|
• Variablenzuweisungen |
|
Das schreiben von Shell Scripts verlangt Wissen über |
|
• Die Shell internen Kontrollstrukturen |
|
• möglichst viele Unix Programme und |
|
• deren Optionen |
Wozu Shell Scripts?
Shell Scripts bieten die Moglichkeit schnell und einfach Aufgaben zu
Automatisieren. Man kann eine Shell auch als eine Programmierspra-
che verwenden. Jedoch unterscheiden sich Scripts von Programmen
in Sprachen wie C betrachtlich. Im Shell Script sind alle Systempro-
gramme direkt verwendbar. Es gibt nur einen Typ von Variablen.
Diese müssen nicht initialisiert werden. Hier ließe sich nun eine lan-
ge Liste von Unterschieden angeben, stattdessen sei nur Folgendes
gesagt: Shell Script eignen sich besonders gut um Aufgaben “quick
and dirty” zu lösen. Ein Systemadministrator, der vor einer größeren
Aufgabe steht - etwa der Erzeugung von 100 durchnummerierten Ac-
counts, wird in der Regel versuchen dies mit einem Script zu losen.
Wird die Aufgabe größer so gehen dieÜberlegungen in Richtung Perl
und letztlich zu einer Compilersprache. Shell Scripts sollte man nicht
verwenden wenn
• die Geschwindigkeit des Programms eine große Rolle spielt.
• komplizierte Aufgaben gelöst werden sollen, die ein stark struk-
turiertes Programmieren erfordern.
• Sicherheitsmechanismen verwendet werden sollen.
• viele Dateien gelesen und geschrieben werden sollen.
• man ein GUI haben will - GUI’s are for wimps anyway.
Verschiedene Shells
sh Bourne shell, die erste Unix Shell von S. R. Bourne
bash Bourne Again Shell, Standard auf Linux, von der FSF
csh Berkeley Unix C Shell, C-ahnliche Syntax, Standard auf BSD
tcsh TENEX C Shell, erweiterte csh
ksh Korn Shell, proprietare Shell von David Korn
pdksh Public Domain Korn Shell
rc Shell fur Plan 9-OS
es Erweiterbare Shell auf Basis von rc
zsh Z Shell von Paul Falstad
ash kleine, POSIX konforme Shell, /bin/sh auf NetBSD
esh Easy Shell, kleine, leicht bedienbare Shell
kiss Karels Interactive Simple Shell
lsh Shell mit DOS Kommandos fur Umsteiger
osh Operators Shell, mit erweiterten Sicherheitsmechanismen
sash Stand-alone Shell, braucht keine Libraries
psh Perl Shell, Perl Syntax
Die Shell unserer Wahl
Shells gibt es sehr viele. Die obige Aufstellung erhebt keinerlei An-
spruch auf Vollständigkeit. Die Bourne Shell (sh), benannt nach ihrem
Erfinder, ist die Mutter aller Shells auf Unix Systemen. Die Bourne
Shell bot schon zu Anfang fast alle Möglichkeiten der Programmie-
rung, die die bash heute bietet. Die interaktive Bedienung war al-
lerding nicht sehr bequem. Dieser Umstand führte zur Geburt der
csh. csh-Scripts folgen einer anderen Syntax, die der Programmier-
sprache C ähnlich ist, als sh-Scripts. Ein weiterer Meilenstein in der
Geschichte der Shells war die Korn Shell ksh, ebenfalls nach ihrem
Erfinder benannt. Diese war ein sehr erfolgreiches proprietares Pro-
dukt. Die bash ist der Versuch einer Zusammenmischung der Vorzüge
verschiedener Shells, wobei die Programmierung in einer erweiterten
sh-Syntax erfolgt.
Im weiteren werden wir unser Hauptaugenmerk auf die bash legen,
die heute wohl die meistverwendete Shell ist. Die meisten unserer
Konstruktionen werden allerdings ohne wieteres auch mit einer sh
funktionieren.
|
Die Login Shell |
|
Nach dem Login landet man auf Unix |
|
Systemen in einer Shell. Welche Shells auf |
|
einem System als mögliche Login Shell |
|
installiert sind steht in der Datei /etc/shells. |
|
$ cat /etc/shells |
|
Die Login Shell jedes Users steht in der Datei |
|
/etc/passwd. |
|
$ grep $USER /etc/passwd | cut -d : -f 7 |
|
Ändern kann man seine Login Shell mit dem |
|
Befehl chsh (aka change shell). Die Standard |
|
Login Shell auf Linux Systemen ist die bash. |
Ein Shell Script schreiben
Ein Shell Script ist eine Textdatei, die Kommandos enthält. Welchen
Texteditor man zur Erzeugung dieser Datei verwendet ist egal, aber...
http://www.thinkgeek.com/images/products/zoom/vi-emacs.jpg
Beginnt diese Textdatei mit der Zeichenfolge
#!/bin/bash
und wird die Datei ausführbar gemacht
$ chmod 755 Dateiname
so kann das Script direkt mit seinem Namen aufgerufen werden.
$ ./scriptname
Das Script wird dann Zeile fur Zeile, vom Interpretor, in unserem Fall
der bash, abgearbeitet. Im Unterschied dazu müssen Programme, die
in Compilersprachen geschrieben sind, erst kompiliert werden, bevor
sie ausgeführt werden konnen.
Wie in allen Programmiersprachen ist das Kommentieren eines Scripts
ein oft ignoriertes Merkmal guten Programmierstils. Zeilen die mit #
beginnen gelten als Kommentare und werden ignoriert.
Kommentare
Zeilen im Script, die mit # beginnen werden als
Kommentar gewertet und beim Ausführen
ignoriert.
# Kommentare koennen helfen die
# Lesbarkeit von Shell Scripts
# zu erhoehen.
Die einzige Ausnahme ist, wenn die ersten zwei
Zeichen des Scripts #! lauten und unmittelbar
nachher der Pfad des Interpreters steht. Dies
macht Script direkt ausfuhrbar und gilt für
beliebige Interpretersprachen.
• #!/bin/bash
• #!/bin/csh
• #!/bin/sh
• #!/usr/bin/perl
Hello World
Das obligate Hello World Programm ist als
Shell Script denkbar einfach. Folgender Text
soll in der Datei helloworld gespeichert sein.
#!/bin/bash
echo "hello world"
Ist es ein Shell Script?
$ file helloworld
Ausführbar machen mit
$ chmod 755 helloworld
und ausführen mit
$ ./helloworld
|
Variablen |
|
• Variablennamen bestehen aus Buchstaben, |
|
und Ziffern. |
|
• Zuweisung erfolgt über |
|
$ VARIABLENNAME=wert |
|
• Auf den Wert zugreifen mit $ |
|
$ echo $VARIABLENNAME |
|
• Unterschiedliche Variablenarten mit |
|
Beispielen |
|
– selbstdefinierte Variablen: |
|
$FOO, $BAR, ... |
|
– systemweite Variablen: |
|
$HOSTNAME, $HOSTTYPE, ... |
|
– built-in Variablen: |
|
$1-$9, $PS1, $PATH, ... |
|
• Variablen exportieren |
|
$ export VARIABLENNAME |
Variablen
Variablenname bestehen aus Buchstaben, und Zahlen, wobei oft auf
Kleinbuchstaben verzichtet wird. Die Zuweisung eines Wertes funk-
tioniert folgendermaßen:
$ VARIABLENNAME=wert
Auf den Wert einer Variable greift man mit $VARIABLENNAME zu.
$ echo $VARIABLENNAME
unset nimmt einer Variable ihren Wert.
unset VARIABLENNAME
In Shell Scripts gibt es nur einen Variablentyp. Man unterscheidet
also nicht wie in anderen Programmiersprachen Integer-, Gleitkomma-
und Stringvariablen. Wenn ein Script davon abhangt, daß in einer
Variable ein Integerwert steht, so ist der Author dafür verantwortlich,
daß dem auch so ist. Variablen müssen nicht initialisiert werden. Dies
bereitet oft Probleme bei Tippfehlern. Folgendes Beispiel führt zu
keiner Fehlermeldung:
$ TEST=Legasthenie
$ echo $TSET
$
Der Befehl set gibt eine Liste, der von der bash gesetzten Variablen.
Unter Anderen befindet sich darunter die Variable PS1 die das Ausse-
hen des Prompts bestimmt. MS-DOS Nostalgiker könnten sich über
folgendes freuen:
$ PS1="C:\> "
C:\>
In Scripts sind vor allem die Variablen $1-$9 von großer Bedeutung.
Diese enthalten die an das Script übergebenen Parameter. Dazu fol-
gendes Beispiel, das in der ausführbaren Datei parameters gespeichert
sein soll:
#!/bin/bash
# Ausgabe des ersten Parameters
echo "Erster Parameter: $1"
# Ausgabe des zweiten Parameters
echo "Zweiter Parameter: $2"
Und das kommt dabei heraus:
$ ./parameters foo bar
Erster Parameter: foo
Zweiter Parameter: bar
|
Subshells |
|
Aus einer Shell kann man eine weitere Shell |
|
starten, welche dann als Subshell der |
|
ursprünglichen Shell bezeichnet wird. |
|
$ bash |
|
Die im Script enthaltenen Kommandos werden |
|
in |
|
• einer Subshell ausgeführt, wenn das Script |
|
mit |
|
$ ./script [&] oder |
|
$ bash script [&] |
|
• in der aktuellen Shell augeführt, wenn das |
|
Script mit |
|
$ . script oder |
|
$ source script |
|
gestartet wird, wobei das otionale & das Script |
|
im Hintergrund startet. |
Subshells, Variablen exportieren
Ein grundsätzliches Konzept des Unix Prozeßmanagments ist, daß
jeder Prozeß von einem Parentprozeß abstammt. Dieser Text entsteht
in einer Instanz des Editors vim. Ein Auszug aus der Ausgabe des
Kommandos pstree soll dieses Konzept verdeutlichen:
$ pstree
init-+-arpwatch
[...]
|-xdm-+-XF86_SVGA
|
‘-xdm---fvwm2-+-FvwmCommandS
|
|-xterm---bash---vim
[...]
[...]
Der Prozeß init ist “Ahne” aller Prozesse. Der Login Manager xdm
wartet auf Benutzerauthentifizierung und startet einen Windowma-
nager fvwm2, aus dem ein xterm gestartet wurde in dem eine bash
läuft, aus der ein vim gestartet wurde.
Eine Subshell ist eine Shell, die aus einer anderen gestartet wurde.
Ruft man ein Shell Script via
$ ./script oder
$ bash script
auf, so wird es in einer Subshell gestartet. Ruft man es via
$ . script oder
$ source script
auf, so werden die darin enthaltenen Befehle in der aktuellen Shell
ausgeführt.
Besonders beachten sollte man, daß die Verwendung von
Pipelines auch innerhalb eines Shell Scripts zu Subshells führt.
Dies ist wichtig, weil Variablenzuweisungen auf eine bash Instanz
beschränkt sind. Die Zuweisung kann in alle Subshells mit dem Befehl
export exportiert werden. Folgende Kommandoabfolgen sollen das
verdeutlichen, wobei man sich durch pstree -h einenUberblick über
die aktuelle Situation machen kann.
$ # Test wird nicht exportieren
$ TEST=rose
$ echo $TEST
rose
$ bash
$ echo $TEST
$ TEST=eros
$ echo $TEST
eros
$ exit
exit
$ echo $TEST
rose
$ unset TEST
$ exit
$ # Test wird exportiert
$ TEST=rose
$ echo $TEST
rose
$ export TEST
$ bash
$ echo $TEST
rose
$ TEST=eros
$ echo $TEST
14
eros
$ exit
exit
$ echo $TEST
rose
$ unset TEST
$ exit
Setzt man also in der Shell eine Variable und startet dann ein Shell
Script, so ist diese Variable - so sie nicht exportiert wurde - im Script
nicht gesetzt.
Spezielle Zeichen und Quoting
Leerzeichen <space> und Tabulatoren <tab> werden als Trennzeichen
verwendet. So werden im folgenden Beispiel das Kommando, die Op-
tionen und die Parameter eines Befehls durch Leerzeichen getrennt:
$ ls -l foo bar
Manchmal ist es erwünscht, einem Zeichen mit spezieller Bedeutung
diese zu nehmen. So kann ein Dateiname auch ein Leerzeichen enthal-
ten. Seit Microsofts Betriebsysteme über die 8.3 Namenskonvention
hinausgekommen sind tritt dieses Phänomen leider gehäuft auf. Als
Beispiel nehmen wir eine Datei mit dem Namen Mein Lied.mp3 an.
Versucht man diese in der Shell zu löschen und schreibt:
$ rm Mein Lied.mp3
so versucht die Shell zwei Dateien mit Namen Mein beziehungsweise
Lied.mp3 zu löschen. Um der Shell mitzuteilen, dass das Leerzeichen
in diesem Fall keine spezielle Bedeutung hat, verwendet man eine der
drei Quoting Methoden. Diese seien im Folgenden anhand unseres
Beispiels angeführt:
$ rm Mein\ Lied.mp3
$ rm ’Mein Lied.mp3’
$ rm "Mein Lied.mp3"
Der Backslash \ (escape character) hebt dabei die spezielle Bedeutung
des unmittelbar folgenden Zeichens auf. Auch wenn dieses Zeichen ein
“newline”, also das Resultat des Betätigens der Return Taste ist. So-
wohl einfache ’’ (single quotes) als auch doppelte Anfuhrungszeichen
"" (double quotes) heben die spezielle Bedeutung der Zeichen dazwi-
schen auf, wobei dies bei den einfachen Anfuhrungszeichen fur alle
Zeichen gilt. Bei doppelten Anführungszeichen bleibt die spezielle
Bedeutung der Zeichen $, ’ und \ erhalten. Vergleiche die Ausgaben
folgender Kommandos:
$ TEST=rose
$ echo "In \$TEST steht \"$TEST\""
In $TEST steht "rose"
$ echo ’In \$TEST steht \"$TEST\"’
In \$TEST steht \"$TEST\"
Im weiteren werden wir noch des öfteren auf spezielle Zeichen stoßen.
Quoting
Quoting wird dazu benutzt, um bestimmten
Zeichen ihre spezielle Bedeutung zu nehmen.
Es gibt drei Moglichkeiten um zu quoten.
\
escape character
’’ single quotes
"" double quotes
Hat ein geistreicher Benutzer eine Datei unter
“Mein Lied * E-Donkey.mp3” abgespeichert, so
kann man diese mit einer der folgenden Zeilen
löschen:
$ rm Mein\ Lied\ \*\ E-Donkey.mp3
$ rm "Mein Lied * E-Donkey.mp3"
Der escape character nimmt dem folgenden
Zeichen seine spezielle Bedeutung. Alle Zeichen
zwischen single quotes sind von ihrer speziellen
Bedeutung befreit. Double quotes lassen den
Zeichen $, ’ und \ ihre spezielle Bedeutung.
Double quotes sind also schwächer als single
quotes.
Befehle zu Gruppen zusammenfassen
Befehle werden durch einen Zeilenumbruch <newline> oder ein Se-
mikolon ; getrennt. Im folgenden zwei Schreibweisen, die das gleiche
liefern:
$ cd /usr/bin
$ ls
bzw.
$ cd /usr/bin; ls
Es gibt zwei Möglichkeiten, Kommandos zu Gruppieren. Runde Klam-
mern () (parantheses) beziehungsweise geschwungene Klammern {}
(curly braces). Befehle die in runden Klammern gruppiert sind, wer-
den in einer Subshell ausgeführt, während in geschwungenen Klam-
mern gruppierte Befehle in der aktuellen Shell ausgeführt werden.
Dazu folgende Beispiele:
$ { FOO=bar; echo "$FOO ist $FOO"; }; echo "$FOO ist $FOO"
$FOO ist bar
$FOO ist bar
$ ( FOO=bar; echo "$FOO ist $FOO" ); echo "$FOO ist $FOO"
$FOO ist bar
$FOO ist
Die runden und die geschwungenen Klammern zählen zu den spezi-
ellen Zeichen. Diese müssen also, so sie im Dateinamen auftreten, mit
Quotes versehen werden.
|
Kommandos Gruppieren |
|
; Trennt verschiedene Kommandos, die in der |
|
gleichen Zeile geschrieben werden. Die |
|
Kommandos werden hintereinander |
|
ausgeführt |
|
() Ein oder mehrere Kommandos innerhalb |
|
runder Klammern werden in einer Subshell |
|
ausgeführt. |
|
{} Ein oder mehrere Kommandos innerhalb |
|
geschwungener Klammern werden als Block |
|
in der aktuellen Shell ausgefuhrt. |
Wild Cards und Pattern Matching
Gehen wir im Folgenden davon aus, dass im aktuellen Verzeichnis
folgende Dateien existieren:
$ ls
glut gut hut mut mutter
* steht fur beliebige Zeichenketten; auch leere.
$ ls mut*
mut mutter
? steht fur genau ein beliebiges Zeichen.
$ ls ?ut
gut hut mut
Alle in eckigen Klammern angegebenen Zeichen dürfen anstelle der
eckigen Klammern vorkommen.
$ ls [gh]ut
gut hut
Ein ! oder ^ verneint diese Auswahl.
$ ls [!gh]ut
mut
Alle in geschwungenen Klammern angegebenen Zeichenketten, die
durch Beistriche getrennt sind, dürfen vorkommen,
$ ls {gl,m}ut
glut mut
Diese Konstrukte sind auch kombinierbar.
$ ls {gl,m}ut*
glut mut mutter
|
Wildcards und Pattern Matching |
|
Folgende Zeichen haben in der Bash spezielle |
|
Bedeutung |
|
* |
|
beliebige Zeichenkette |
|
? |
|
genau ein beliebiges Zeichen |
|
[ ] |
|
genau eines der genannten Zeichen |
|
[^ ] |
|
genau ein nicht genanntes Zeichen |
|
oder [! ] |
|
{ , } |
|
genau eine der Zeichenketten |