Bash: Unterschied zwischen den Versionen

Aus xinux.net
Zur Navigation springen Zur Suche springen
Zeile 1.158: Zeile 1.158:
  
 
=bash specials=
 
=bash specials=
</pre>
+
<pre>
 
[[ is bash's improvement to the [ command. It has several enhancements that make it a better choice if you write scripts that target bash. My favorites are:
 
[[ is bash's improvement to the [ command. It has several enhancements that make it a better choice if you write scripts that target bash. My favorites are:
  

Version vom 23. März 2017, 21:18 Uhr

Prinzip

Auf der Konsole werden die Befehle aneinander gereiht, indem zwischen den Befehlen ein ; eingefügt wird.

thomas@dozent:~$ date ; hostname ; pwd
Mon Dec 15 08:59:13 CET 2003
dozent
/home/thomas

Neben dieser interaktiven Eingabe gibt es auch die Möglichkeit, dass die Shell die Kommandos aus einer Datei lesen kann.

Der Inhalt eines (z.B. mit dem vi erstellten) Shellskriptes:

thomas@dozent:~/bin$ cat skript
date
hostname
pwd

Die Ausgabe des Shellskriptes

thomas@dozent:~/bin$ ./skript
Mon Dec 15 09:07:22 CET 2003
dozent
/home/thomas/bin

Struktogramm nach Nassi-Shneiderman

Ausgabe Datum
Ausgabe Rechnername
Ausgabe Arbeitszverzeichnis

Der Interpretor in der Windowswelt

Endung Interpretor
.pl Perl
.py Python
.bat command.com
.cmd cmd.exe

Der Interpretor in der Linuxwelt

Unter Unix/Linux entscheidet nicht die Suffix welchem Interpretor ein Skript übergeben wird, da ja in der Regel keine Suffix vorhanden ist. Die Art wird durch die Datei bestimmt, und zwar durch die ersten Bytes einer Datei. Wenn wir unser Skript mit

thomas@dozent:~file skript
skript: ASCII text

untersuchen sehen wir, dass es als normaler ASCII Text interpretiert wird. Bei einem Programmaufruf wird es einfach der aktuellen Shell übergeben (es gibt ausser der bash noch andere Shells z.B. bourne-shell (bsh), korn-shell (ksh), ash). Um sicherzugehen, dass es der richtigen Shell übergeben wird, fuegt man an den Skriptanfang einfach ein #! an, in unserem Fall ein #!/bin/bash für die Bourne-Again Shell. Das bedeutet ,dass das Programm welches hinter dem #! (Gobang Operator) steht der Interpretor ist, dem das Skript übergeben wird.

#!/bin/bash
date
hostname
pwd

Selbst in der C shell ist somit sichergestellt ,dass das Skript der Bash übergeben wird

thomas@dozent:~file skript
skript: Bourne-Again shell script text executable

Ablauf eines Shell-Skriptes

  • Starten einer Subshell
  • Lesen der Skriptdatei von der Subshell (zeilenweise)
  • Kommandos werden nacheinander abgearbeitet
  • Beenden der Subshell und Rückkehr zur aufrufenden Shell

Möglichkeiten ein Shellskript aufzurufen

bash skript (r)

  • Starten einer Subshell
  • Lesen der Skriptdatei von der Subshell
  • Kommandos werden nacheinander abgearbeitet
  • Beenden der Subshell und Rückkehr zur aufrufenden Shell

(Die Shell, die das Skript abarbeitet, kennt den Namen des Shellskriptes)

bash < skript (r)

  • Starten einer Subshell
  • Lesen der Skriptdatei von der Subshell
  • Kommandos werden nacheinander abgearbeitet
  • Beenden der Subshell und Rückkehr zur aufrufenden Shell

(Die Shell, die das Skript abarbeitet, kennt den Namen des Shellskriptes nicht)

./skript (rx)

  • Starten einer Subshell
  • Lesen der Skriptdatei von der Subshell
  • Kommandos werden nacheinander abgearbeitet
  • Beenden der Subshell und Rückkehr zur aufrufenden Shell

(Die Shell, die das Skript abarbeitet, kennt den Namen des Shellskriptes, es muss zusätzlich das Ausführungsrecht gesetzt sein.)

exec ./skript (rx)

  • Die Subshell ersetzt die aktuelle Shell (überlädt die aktuelle Shell)
  • Lesen der Skriptdatei von der Subshell
  • Kommandos werden nacheinander abgearbeitet
  • Beenden der Subshell; danach ist der Prozess der aufrufenden Shell beendet.

source skript oder . skript (r)

  • Lesen der Skriptdatei von der aktuellen Shell
  • Der interaktive Modus der aktuellen Shell wird „unterbrochen“
  • Kommandos werden nacheinander abgearbeitet

(Es wird kein neuer Prozess gestartet; Variablen haben in dieser Shell Gültigkeit)

Variablen

Skalare Variablen

Eine Variable stellt einen Platzhalter dar. Man spricht von Variablensubstitution, wenn anstatt der Variablen deren Wert bzw. Inhalt ausgegeben wird.Eine Variable muss im Gegensatz zu Programmiersprachen, die kompiliert werden, nicht deklariert werden, da der Interpretor zu Laufzeit den Speicherplatz allokiert (Belegung des Speicherplatzes zur Laufzeit).

Die Definition einer Variablen erfolgt durch die Nennung, gefolgt von einem = Zeichen, gefolgt von dem Inhalt, der zugewiesen wird. Wichtig : zwischen der Variablen, dem = und dem Wert darf kein Leerzeichen stehen. Man benutzt häufig Grossbuchtstaben, es sollten keine – benutzt werden.

Definition der Variable

thomas@dozent:~$ GLAS=bier

Ausgabe des Variableninhalts

thomas@dozent:~$ echo $GLAS
bier

Definition der Variable:

thomas@dozent:~$ UNIXSYSTEM=/usr
Variablensubstititution:
thomas@dozent:~$ ls -ld  $UNIXSYSTEM
drwxr-xr-x 11 root root 4096 2008-10-29 23:58 /usr

Feld Variablen

Definition der Feld Variablen

thomas@dozent:~/bin$ farbe=(blau gelb gruen rot)

Ausgabe des Inhalts der Feld Variablen

thomas@dozent:~/bin$ echo ${farbe[0]}
blau
thomas@dozent:~/bin$ echo ${farbe[1]}
gelb
thomas@dozent:~/bin$ echo ${farbe[2]}
gruen
thomas@dozent:~/bin$ echo ${farbe[3]}
rot

Der Exportbefehl

Die Variable wird nicht automatisch an Kindprozess weitergegeben. Mit export wird eine Variable markiert, sodass sie in die Prozessumgebung übernommen wird um beim Forken auch dem Kindprozess zur Verfügung zu stehen. Alle exportierten Variablen können mit dem Befehl printenv oder env angezeigt werden. Wirklich alle Shellvariablen werden mit set angezeigt.

Kommandosubstitution

Bei der Kommandosubstitution wird ein Kommando mit seinem Rückgabewert ersetzt. Es existieren zwei Varianten

$(date) 
`date`

Bei der ersten Variante besteht die Möglichkeit zu schachteln:

thomas@dozent:~$ cd $(echo /home/$(whoami))
thomas@dozent:~$ cd `echo /home/`whoami``
thomas@dozent:~$ date -u "+%X"
09:29:41
thomas@dozent:~$ MOMENT=$(date -u "+%X")
thomas@dozent:~$ echo $MOMENT
09:29:58
thomas@dozent:~$ echo $MOMENT
09:29:58

Abgrenzen von Variablen

Folgt einem Dollarzeichen $ ein Variablenname oder eine öffnende geschweifte Klammer ${...}, so spricht man von einer Variablen- bzw. Parameterexpansion. Die geschweiften Klammern dienen zur Gruppierung und sind bei skalaren Variablen, die nicht per Parameterexpansion behandelt werden sollen, nicht notwendig.

thomas@dozent:~$ SUX=eins
thomas@dozent:~$ TUX=zwei
thomas@dozent:~$ SUXTUX=drei

thomas@dozent:~$ echo $SUX
eins
thomas@dozent:~$ echo ${SUX}
eins
thomas@dozent:~$ echo $SUXTUX
drei
thomas@dozent:~$ echo ${SUX}TUX
einsTUX
thomas@dozent:~$ echo ${SUX}${TUX}
einszwei

Weitere Mechanismen zur Parameterexpansion

Die weiteren Mechanismen zur Parameterexpansion manipulieren den Inhalt von Variablen. "FARBE" bezeichnet nachfolgend den Variablennamen und "rot" steht entweder für eine Zeichenkette oder für eine Variable, die selbst wieder eine Parameter-, Kommando, Tildeexpansion oder eine arithmetische Berechnung beinhalten kann.

  • ${FARBE:-rot}

Wenn die Variable $FARBE definiert ist, wird der Inhalt zurückgeliefert; wenn sie nicht definiert ist, wird rot zurückgeliefert

thomas@dozent:~$ unset FARBE
thomas@dozent:~$ echo ${FARBE:-rot}
rot
thomas@dozent:~$ FARBE=gruen
thomas@dozent:~$ echo ${FARBE:-rot}
gruen
thomas@dozent:~$ echo $FARBE
gruen
  • ${FARBE:=rot}

Wenn die Variable $FARBE definiert ist, wird der Inhalt zurückgeliefert, wenn sie nicht definiert ist, wird rot zurückgeliefert und die Variable $FARBE wird mit dem Wert rot belegt (somit ist sie definiert).

thomas@dozent:~$ unset FARBE
thomas@dozent:~$ echo ${FARBE:=rot}
rot
thomas@dozent:~$ echo $FARBE
rot
  • ${FARBE:?keine farbe gesetzt}

Wenn die Variable $FARBE definiert ist, wird der Inhalt zurückgeliefert, wenn sie nicht definiert ist, wird 'keine farbe' als Fehlermitteilung ausgegeben. Der Rückgabewert ist dann auch ungleich 0.

thomas@dozent:~$ unset FARBE
thomas@dozent:~$ echo ${FARBE:?keine farbe}
bash: FARBE: keine farbe
thomas@dozent:~$ echo $?
1
thomas@dozent:~$ FARBE=silber
thomas@dozent:~$ echo ${FARBE:?keine farbe}
silber

  • ${FARBE:+rot}

Wenn die Variable $FARBE definiert ist, wird rot zurückgeliefert, ansonsten nichts.

thomas@dozent:~$ unset FARBE
thomas@dozent:~$ echo ${FARBE:+rot} 

thomas@dozent:~$ FARBE=gelb
thomas@dozent:~$ echo ${FARBE:+rot}
rot
thomas@dozent:~$ echo $FARBE
gelb

  • ${FARBE:4}

Der Inhalt der Variable wird ab der 4 Position bis zum Ende ausgegeben. Es wird ab 0 gezählt.

thomas@dozent:~$ FARBE=rotweis
thomas@dozent:~$ echo ${FARBE:3}
weis

  • ${FARBE:5:3}

Ab der 5 Position werden 3 Zeichen ausgegeben. Es wird ab 0 gezählt.

thomas@dozent:~$ FARBE=schwarzbraun
thomas@dozent:~$ echo ${FARBE:5:3}
rzb
  • ${#FARBE}

Die Anzahl Zeichen der Variable wird ausgegeben

thomas@dozent:~$ FARBE=schwarzbraun
thomas@dozent:~$ echo ${#FARBE}
12
  • ${FARBE#rot}

Wenn das Wort rot am Anfang der Variable steht wird der Rest der Variable ausgegeben . Wenn dies nicht so ist wird die Variable ausgegeben.

thomas@dozent:~/bin$ FARBE=rotweis
thomas@dozent:~/bin$ echo ${FARBE#rot}
weis
  • ${PROGRAM##*/}

Werden ## Kreuze angeben so wird die längstmögliche Ersetzung vorgenommen und der Rest wird ausgeben.

thomas@dozent:~/bin$ PROGRAM=/usr/bin/passwd
thomas@dozent:~/bin$ echo ${PROGRAM#*/}
usr/bin/passwd
thomas@dozent:~/bin$ echo ${PROGRAM##*/}
passwd
  • ${FARBE%weis}

Wenn das Wort rot am Ende der Variable steht wird der restliche Anfang der Variable ausgegeben. Wenn dies nicht so ist wird die Variable ausgegeben.

thomas@dozent:~/bin$ FARBE=rotweis
thomas@dozent:~/bin$ echo ${FARBE%weis}
rot
  • ${PROGRAM%%*/}

Werden %% (2 Prozentzeichen) angeben, so wird die längstmögliche Ersetzung vorgenommen und der Anfang wird ausgeben.

thomas@dozent:~/bin$ FARBE=rotweisrot
thomas@dozent:~/bin$ echo ${FARBE%o*}
rotweisr
thomas@dozent:~/bin$ echo ${FARBE%%o*}
r
  • ${FARBE/rot/blau}

Ersetzen eines Musters. Kommt in der Variable FARBE ein rot vor, wird dies durch blau ersetzt.

  • ${FARBE//rot/blau}

Ersetzen eines Musters. Kommen in der Variable FARBE mehrere rot vor, werden sie durch blau ersetzt.

thomas@dozent:~/bin$ FARBE=rotweisrot
thomas@dozent:~/bin$ echo ${FARBE/rot/blau}
blauweisrot
thomas@dozent:~/bin$ echo ${FARBE//rot/blau}
blauweisblau

Besondere Dateien

  • /etc/profile

Wird beim Anmelden automatisch ausgeführt. Sie gilt für alle.

  • ~/.bash_profile

Wenn diese Datei existiert, wird sie beim Anmelden automatisch ausgeführt; sonst springe zu ~/.bash_login

  • ~/.bash_login

Wenn diese Datei existiert wird sie beim Anmelden automatisch ausgeführt ;sonst springe zu ~/.profile

  • ~/.profile

Wird beim Anmelden automatisch ausgeführt wenn die beiden obigen nicht existieren.

  • ~/.bashrc

Wird beim starten jeder Bash automatisch ausgeführt.

  • ~/.bash_history

Enthält die während der letzten Sitzung eingegebenen Befehle

  • ~/.bash_logout

Wird beim Abmelden ausgeführt

Wichtige Systemvariablen (Auswahl)

  • PATH

Pfad, in dem nach ausführbaren Programmen gesucht wird

  • HOME

Heimatverzeichnis

  • BASH

Pfadname der aktuellen Shell

  • PWD

Aktuelles Verzeichnis

  • OLDPWD

Letztes aktuelles Verzeichnis vor cd

  • PS1 ,PS2 , PS3

Eingabeprompt 1 2 und 3

  • UID

Benutzerkennung

  • EDITOR

Standardeditor (wenn nicht belegt, dann vi)

  • MAIL

Dort werden die Mails gespeichert

  • HOSTNAME

Name des Rechners

Stellungsparameter (Spezielle Variablen)

Den Stellungsparametern $1, $2, ..., $9 werden bei ihrer Definition nach ihrer Reihenfolge Werte zugewiesen. Die Definition erfolgt mit dem Kommando set', einem Skript - oder Prozeduraufruf .

Ihr Geltungsbereich ist lokal, d.h. sie gelten nur in der aktuellen bash und können nicht exportiert werden.

Belegen der Variablen mittels set

thomas@dozent:~/bin$ set der fck ist eine klasse Mannschaft
thomas@dozent:~/bin$ echo $1 $2 $3
der fck ist
thomas@dozent:~/bin$ echo $5 $6 $7
eine klasse Mannschaft
thomas@dozent:~/bin$ set unser fcs ist aber auch ok
thomas@dozent:~/bin$ echo $1 $2 $3
unser fcs ist

Belegen der Variablen durch einen Skriptaufruf

Die Variablen werden automatisch beim Skriptaufruf belegt, indem sie als Argumente dem Skript übergeben werden.

thomas@dozent:~/bin$ cat skript1
#!/bin/bash
echo "1         parameter $1"
echo "2         parameter $2"
echo "3         parameter $3"
echo "4         parameter $4"
echo "alle     parameter $*"
echo "alle     parameter $@"
echo "anzahl    parameter $#"
echo "skriptname      $0"
echo "PID des skriptes      $$"
echo "Rückgabewert letztes Kommando     $?"


thomas@dozent:~/bin$ ./skript1 blau gruen gelb rot
1            parameter blau
2            parameter gruen
3            parameter gelb
4            parameter rot
alle         parameter blau gruen gelb rot
alle         parameter blau gruen gelb rot
anzahl       parameter 4
skriptname             ./skript1
PID des skriptes       1019
Rückgabewert letztes Kommando    0

Systemparameter

Der Begriff Systemparameter bezeichnet eine Variable, die von der bash automatisch mit Werten versorgt wird. Die aktuellen Werte können vom Benutzer abgefragt werden.

Einige wichtige Systemparameter:


$$ PID des laufenden Prozesses
$! PID des letzten Hintergrundprozesses
$- Liste der für die bash gesetzten Schalter
$? Exitstatus des letzten Kommandos
$# Anzahl der Stellungsparameter
$* Liste der Stellungsparameter
$0 Aktueller Prozessname

shift-Kommando

Das Kommando bewirkt die Verschiebung aller Stellungsoperanden um eine Stelle nach links. Auf diese Weise ist nach der Anwendung des shift-Kommandos der Wert des 10ten Parameters in $9, der ursprüngliche Wert von $1 ist verloren. Die Stellenanzahl, um die verschoben wird, kann angegeben werden, z. B. bewirkt der Befehl shift 4 das Verschieben um 4 Stellen nach links.

thomas@dozent:~$ set hallo ihr guten admins von der schule hier
thomas@dozent:~$ echo $1 $2 $3 $4
hallo ihr guten admins
thomas@dozent:~$ shift
thomas@dozent:~$ echo $1 $2 $3 $4
ihr guten admins von
thomas@dozent:~$ shift 3
thomas@dozent:~$ echo $1 $2 $3 $4
von der schule hier

read-Kommando

Mit read wird eine Eingabezeile eingelesen und deren Inhalt Variablen zugewiesen. Die Eingabe wird anhand der Trennzeichen in einzelne Token zerlegt und der Reihe nach den Variablen zugewiesen. Stehen mehr Token zur Verfügung als Variablen, so wird die letzte Variable mit allen noch nicht zugewiesenen Token belegt; stehen weniger Token bereit, bleibt der Inhalt der überschüssigen Variablen leer:

#!/bin/bash
echo "wie heißen sie?"
read NAME VORNAME
echo "Sie heißen $VORNAME $NAME"

read wird in dieser Form sehr selten benutzt da der automatische Charakter von shell Skripten dadurch verloren geht.

Einfache Verzweigungen

In Abhängigkeit vom Returncode eines Befehls oder einer Pipe kann mit den Sonderzeichen && und || eine Verzweigung durchgeführt werden.

echo das ist sux1. > sux1
rm sux1 && echo sux1 ist geloescht!
rm sux1 || echo sux1 konnte nicht geloescht werden.

Der Befehl nach && wird dabei nur ausgeführt, wenn der Returncode des vorherigen Befehls oder der vorherigen Pipe 0 war, also der Befehl vor der Pipe fehlerfrei ausgeführt wurde. Ist ein Befehl vor einer Pipe nicht erfolgreich (Returncode != 0), wird der Befehl nach || ausgeführt.

Endestatus

Nach der Ausführung eines Befehls wird ein Returncode (Endestatus) zurückgeliefert. Mit Hilfe des Returncodes lässt sich feststellen, ob der letzte Befehl fehlerfrei ausgeführt wurde.

Returncode = 0 Letzter Befehl wurde fehlerfrei ausgeführt.
Returncode ≠ 0 Letzter Befehl wurde nicht fehlerfrei ausgeführt.

Das Kommando true liefert den Returncode 0, false liefert den Returncode ≠ 0 . Bei einigen Befehlen wird die zurückgelieferte Fehlermeldung (Returncode ≠ 0) noch weiter differenziert (siehe man fsck). In der Systemvariablen $? ist der aktuelle Returncode abgelegt und kann vom Benutzer abgefragt werden. Bsp.:

thomas@dozent:~fsck asasas
thomas@dozent:~echo $?
16

oder

thomas@dozent:~/bin$ ping www.xinux.de -w 1 -c 1 > /dev/null 2>&1
thomas@dozent:~/bin$ ping 172.20.103.2  -w 1 -c 1 > /dev/null 2>&1
thomas@dozent:~/bin$ echo $?
0

Erkenntnis: Rechner ist erreichbar

thomas@dozent:~/bin$ ping 172.20.103.99  -w 1 -c 1 > /dev/null 2>&1
thomas@dozent:~/bin$ echo $?
1

Erkenntnis: Rechner ist nicht erreichbar

thomas@dozent:~/bin$ touch bohnen
thomas@dozent:~/bin$ rm bohnen 2> /dev/null
thomas@dozent:~/bin$ echo $?
0

Erkenntnis: Löschen war erfolgreich

thomas@dozent:~/bin$ rm bohnen 2> /dev/null
thomas@dozent:~/bin$ echo $?
1

Erkenntnis: Löschen war nicht erfolgreich

test-Kommando

Der eingebaute Befehl test gibt sein Ergebnis als Returncode zurück. Dieser Befehl wird daher oft in Kontrollstrukturen der bash zur Verzweigung verwendet.

Es existieren 2 Schreibweisen:

test bedingung
[ bedingung ]

Ist das von bedingung zurückgelieferte Ergebnis wahr, wird der Returncode 0 übergeben, sonst ungleich 0. bedingung kann ein numerischer Vergleich, ein Stringvergleich, eine Prüfung von Objekteigenschaften oder eine beliebige Kombination der vorhergehenden sein.

Numerischer Vergleich

test zahl1 -op zahl2			(op: eq, ne, lt, gt, ge, le)
thomas@dozent:~/bin$ ZAHL=4
thomas@dozent:~/bin$ test $ZAHL -eq "4" && echo stimmt


-eq gleich
-ne ungleich
-lt kleiner
-gt größer
-ge größer oder gleich
-le kleiner oder gleich

Stringvergleich

test string1 = string2
test string1 != string2
test –n string   oder  test string		(Länge von string größer null)
test –z string	 				(Länge von string gleich null)
thomas@dozent:~/bin$ HOBBIT=frodo
thomas@dozent:~/bin$ test $HOBBIT = "bilbo" && echo frodo
thomas@dozent:~/bin$ test $HOBBIT = "bilbo" && echo bilbo
thomas@dozent:~/bin$ HOBBIT=bilbo
thomas@dozent:~/bin$ test $HOBBIT = "bilbo" && echo bilbo
bilbo

Objekteigenschaften

test -op objekt 	    	
d Directory
f Datei
s nicht leere Datei
r leserecht auf das Objekt
w schreibrecht auf das Objekt
x ausführungsrecht auf das Objekt
thomas@dozent:~/bin$ test -f /etc/shadow && echo sicheres system
sicheres system
thomas@dozent:~/bin$ test -r /etc/shadow || echo so ist es richtig
so ist es richtig

Prioritäten bei logischen Verknüpfungen

runde Klammer Negation AND OR
() ! -a -o


	test \( -r dat -o -w dat \)
	test ! \( -r dat -o -w dat \)
	test $name;echo $?
thomas@dozent:~/bin$ ! test -r /etc/shadow &&  echo so ist es richtig
so ist es richtig

Der if-Block

if programm
then 
  anweisung1
else
  anweisung2
fi

Der if-Block verzweigt in Abhängigkeit des Returncodes des Programmes das ausgeführt wird. Ist der Returncode gleich null, wird Anweisung1 ausgeführt; ist der Returncode ungleich null, wird Anweisung2 ausgeführt.

If.jpg


Der else-Zweig kann ausgelassen oder durch einen elif-Zweig ersetzt werden. Der elif-Zweig ist eine Besonderheit bei Verschachtelungen.

thomas@dozent:~/bin$ cat rechnertest
#!/bin/bash
if ping -c 1 -w 1 $1 > /dev/null 2>&1
then
echo "rechner lebt"
else
echo "rechner ist tot"
fi
thomas@dozent:~/bin$ ./rechnertest 172.20.103.1
rechner lebt
thomas@dozent:~/bin$ ./rechnertest 172.20.103.99
rechner ist tot

Variante ohne else

homas@dozent:~/bin$ cat rechnertest.spar
#!/bin/bash
if ping -c 1 -w 1 $1 > /dev/null 2>&1
then
echo "rechner lebt"
fi
thomas@dozent:~/bin$ ./rechnertest.spar 172.20.103.1
rechner lebt
thomas@dozent:~/bin$ ./rechnertest.spar 172.20.103.99

Sonderform mit elif

thomas@dozent:~/bin$ cat el
#!/bin/bash
echo -n "wie heissen Sie :"
read NAME
if test $NAME = "thomas"
then
       echo "hallo meister"
elif test $NAME = "martin"
       then
       echo "hallo vizemeister"
       else
       echo "hallo wurm"
fi
thomas@dozent:~/bin$ ./el
wie heissen Sie :thomas
hallo meister
thomas@dozent:~/bin$ ./el
wie heissen Sie :martin
hallo vizemeister
thomas@dozent:~/bin$ ./el
wie heissen Sie :suxer
hallo wurm

Der case-Block

Der case-Block dient der Durchführung eines Stringvergleiches. Er wird sequentiell von oben nach unten abgearbeitet. Bei Übereinstimmungen mit einem definierten Muster wird der darauf folgende Befehl ausgeführt (bzw. die darauf folgende Befehlsliste). Bei der ersten gefundenen Übereinstimmung terminiert der case-Block.

Bei der Definition des Musters können die Sonderzeichen der bash zur Dateinamengenerierung verwendet werden. Zusätzlich kann | für eine logische OR-Verknüpfung benutzt werden.

thomas@dozent:~/bin$ cat case1
#!/bin/bash
case $1 in
       rock|rocknroll)
               echo "stones sind gut"
       ;;
       schlager)
               echo "guildo ist ein gott"
       ;;
       volks)
               echo "was an der waffel?"
       ;;
       *)
               echo "kein bock auf musik"
       ;;
esac
thomas@dozent:~/bin$ ./case1 rock
stones sind gut
thomas@dozent:~/bin$ ./case1 rocknroll
stones sind gut
thomas@dozent:~/bin$ ./case1 schlager
guildo ist der meister 
thomas@dozent:~/bin$ ./case1 volks
was an der waffel?
thomas@dozent:~/bin$ ./case1 kllkjl
kein bock auf musik

Rechnen mit der Bash

  • $(())
  • expr
  • bc

Arithmetische Substitution

Die $(()) oder $[ ] ist die arithmetische Erweiterungsmethode der Bash.

thomas@dozent:~/bin$ echo $((7+5))
12
thomas@dozent:~/bin$ echo $((7*5))
35
thomas@dozent:~/bin$ echo $((7/5))
1
thomas@dozent:~/bin$ echo $((7-5))
2
thomas@dozent:~/bin$ echo $((7%5))
2
thomas@dozent:~/bin$ ZAHL=5 ; ZAHL=$(($ZAHL + 1)) ; echo $ZAHL
6

Der expr-Befehl

Erlaubt die Durchführung komplexer Stringoperationen und Ganzzahlarithme­tik.

thomas@dozent:~/bin$ expr 7 + 5
12
thomas@dozent:~/bin$ expr 7 \* 5
35
thomas@dozent:~/bin$ expr 7 / 5
1
thomas@dozent:~/bin$ expr 7 – 5
2
thomas@dozent:~/bin$ expr 7 % 5
2
thomas@dozent:~/bin$ ZAHL=5 ; ZAHL=$(expr $ZAHL + 1) ; echo $ZAHL
6

bc - interaktiver Taschenrechner

thomas@dozent:~/bin$ bc
bc 1.06
Copyright 1991-1994, 1997, 1998, 2000 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
12 * 3
36
quit

Mit der Option -l wird die mathematische Bibliothek eingeschaltet

thomas@dozent:~/bin$ bc -l
bc 1.06
Copyright 1991-1994, 1997, 1998, 2000 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
7 / 5
1.40000000000000000000
quit

Da bc auch von STDIN lesen kann, kann man es auch in Shellskripten benutzen.

thomas@dozent:~/bin$ echo 7/5 | bc -l
1.40000000000000000000
thomas@dozent:~/bin$ ZAHL=5 ; ZAHL=$(echo $ZAHL+1 | bc -l) ; echo $ZAHL
6

Die while-Schleife

Die while-Schleife wird so lange durchlaufen, bis der Returncode ungleich null ist.

thomas@dozent:~/bin$ cat proggi
#!/bin/bash
while [ $# -ge 1 ]
do
 echo $1
 echo $#
 shift
done


thomas@dozent:~/bin$ ./proggi blau gelb gruen
blau
3
gelb
2
gruen
1

Darstellung als Struktogramm nach Nassi / Shneiderman:

While.jpg

Beispiel:

#!/bin/bash
COUNTER=$1
while [ $COUNTER -ge 1 ]
 do
   echo $COUNTER
   COUNTER=$(($COUNTER-1))
done
echo "BUMM BUMM"
thomas@dozent:~/bin$ ./countdown 5
5
4
3
2
1
BUMM BUMM

Sonderform von while

Die Variable SUX wird nacheinander mit jeder Zeile der Datei /etc/passwd belegt, bis die letzte Zeile der Datei /etc/passwd erreicht ist.

#!/bin/bash
while read SUX
do
echo $SUX
done  <  /etc/passwd

Selbstgebautes cat

#!/bin/bash
while read ZEILE
do
echo $ZEILE
done < $1

Selbstgebautes tac

#!/bin/bash
COUNT=0
while read ZEILE
do
FELD[$COUNT]=$ZEILE
COUNT=$(($COUNT+1))
done < $1

while [ $COUNT -ge 0 ]
do
echo ${FELD[$COUNT]}
COUNT=$(($COUNT-1))
done

Die until-Schleife

Die until-Schleife wird so lange durchlaufen, bis der Returncode der Abbruchbedingung gleich null ist.

	until test $# -eq 0“
	do
	     echo $1
	     shift
	done

Darstellung als Struktogramm nach Nassi / Shneiderman:

Until.jpg

Die for-Schleife

Bei der for-Schleife wird bei jedem Durchlauf der Schleifenvariablen ein Wert aus einer angegebenen Liste zugewiesen; die Liste wird dabei von links nach rechts durchlaufen. Nach der letzten Wertzuweisung terminiert die for-Schleife.

thomas@dozent:~/bin$ cat fussball
#!/bin/bash
for CLUB in fck bvb fcs
do
echo $CLUB
done
thomas@dozent:~/bin$ ./fussball
fck
bvb
fcs

Darstellung als Struktogramm nach Nassi / Shneiderman:

For.jpg

Wird keine Liste angegeben, wird standardmäßig die Liste der Stellungsoperanden benutzt. Folgende Anweisungen sind äquivalent

!#/bin/bash
for LAUF in $*
do
echo $LAUF
done

Kurzform :

for LAUF 
do
echo $LAUF
done

Besonderheiten der for – Schleife

Der Stern würde durch alle Dateien des aktuellen Verzeichnisses ersetzt werden. Daraus folgt, dass der Name jeder Datei nacheinander in die Variable LAUF geschreiben wird. Die Anzahl der Schleifenläufe ist identisch mit der Anzahl von Dateien.

#!/bin/bash
for LAUF in *
do
echo $LAUF
done

Hier die Variante mit einem grossen K

#!/bin/bash
for LAUF in K*
do
echo $LAUF
done

Mit Bash-Version 2.0.4 wurde die for-Schleife um eine an die Programmier-sprache C angelehnte Syntaxvariante erweitert:

for ((Initialisierung der Laufvaribale; Abbruchbedingung; Veränderung der Laufvariable))
do 
Kommando
done 
thomas@dozent:~/bin$ cat foor
#!/bin/bash
for ((I=1;I<5;I++))
do
echo $I
done
thomas@dozent:~/bin$ ./foor
1
2
3
4

Steuerung der Ablaufanweisungen

exit n

Der aktuelle Prozess und damit auch die bash werden abgebrochen. Für n kann eine Zahl zwischen 0 und 255 angegeben werden; damit kann der Returncode des Prozesses festgelegt werden, der an den aufrufenden Prozess übergeben wird.

Zur Schleifensteuerung können die Befehle continue und break verwendet werden. Sie dürfen nur zwischen den Schlüsselwörtern do und done stehen.

continue n

Der aktuelle Schleifendurchlauf wird abgebrochen, um mit dem nächsten Durchlauf zu beginnen. Bei Verschachtelungen kann durch Angabe einer Ganzzahl in der n-ten Schleifenebene angesetzt werden.

#!/bin/bash
for CLUB in fck bvb bayern fcs
 do
   if [ $CLUB = "bayern" ]
     then
       echo "zeig ich nicht an"
       continue
     exit
   fi
echo $CLUB
done
thomas@dozent:~/bin$ ./fussball
fck
bvb
zeig ich nicht an
fcs

break n

Die aktuelle Schleife wird abgebrochen, danach wird mit der ersten Anweisung nach der Schleife weitergemacht. Bei Verschachtelungen wird auf der n-ten Schleifenebene aufgesetzt.

Anmerkung: Ein sinnvoller Einsatz dieser Konstrukte liegt in der Behandlung von Ausnahmen (Fehler). Intensiver Einsatz macht die Programme unleserlich und schwer kontrollierbar. Daher ist eine sparsame Verwendung empfehlenswert.


#!/bin/bash 
while true
do
    test -f /tmp/sux && break  
    echo "unn weiter"
    sleep 3
done
echo  "und tschuess"

Funktionen

Eine Funktion ist ein Name für ein Kommando oder für eine Gruppe von Kommandos. Funktionen werden vorrangig in Shellskripten verwendet, um wiederkehrende Kommandosequenzen nicht ständig neu schreiben zu müssen. Ein großer Vorteil von Funktionen ist, dass sie sich in einer Datei speichern lassen und diese Datei von anderen Skripten geladen werden kann (sourcen).

Eine Funktion wird wie folgt definiert: Format: [function] Funktionsname() { Kommando; [Kommando;] }

Bei der Verwendung von Funktionen sind einige Regeln zu befolgen:

1. Deckt sich der Name der Funktion mit einem builtin-Kommando, wird immer die Funktion ausgeführt und niemals das Kommando. Ebenso verdeckt ein Funktionsname ein gleichnamiges Kommando. (ausser man benutzt das Kommando builtin)

2. Die Funktion muss vor ihrer Verwendung definiert sein.

3. Eine Funktion läuft in der aktuellen Umgebung, d.h. alle Variablen der Umgebung sind sichtbar und alle Variablen, die in der Funktion definiert wurden, sind auch außerhalb sichtbar:

4. Wird eine Funktion mittels "exit" verlassen, wird auch der rufende Prozess beendet:

5. Der Rückgabewert einer Funktion ist der Rückgabewert des letzten in ihr gestarteten Kommandos

6. Funktionen sind nur in der Shell ihrer Definition bekannt

7. return bewirkt das Verlassen der Funktion, auch mit return kann ein Rückgabewert mitgegeben werden

Beispiel

#!/bin/bash
function sux () 
{ 
echo "ich bin eine suxer funktion"
} 
sux

Signalbehandlung

Signale bei der Programmierung der bash

Es existieren verschiedene Möglichkeiten, auf welchem Wege Signale gesendet werden können:

  1. von aussen:
    1. Benutzer (< DEL >, < CTRL >|, etc.)
    2. Prozesse (kill, alarm)
  2. von innen:
    1. Programmfehler (Adressfehler, ungültiger Befehl, Division durch 0, etc.)

Signale dienen der Interprozesskommunikation. Diese Nachrichten beschränken sich allerdings auf die Übertragung eines einzigen Wertes.

Von den Befehlen trap und kill werden folgende Signale verwendet:

Signalnummer Bedeutung
0 Beenden von bash
SIGHUP 1 Logoff von einer Datensichtstation
SIGINT 2 Drücken der Taste < DEL >
SIGQUIT 3 Drücken der Taste < CTRL > + c
SIGKILL 9 kill: unbedingter Prozessabbruch
SIGTERM 15 Programmbeendigung

Reaktion eines Prozesses auf den Empfang eines Signals

  • Der Prozess beendet sich (meist Standardeinstellung).
  • Der Prozess ignoriert das Signal. (Ausnahme: SIGKILL)
  • Der Prozess fängt das Signal ab, d.h. er leitet eine selbst definierte Reaktion ein.

Der Befehl trap

Funktionen:

  1. Signalbehandlung setzen
trap 'rm *.tmp' 0 	(Nach Beendigung der bash werden die betreffenden temporären Dateien gelöscht)
trap 'who; exit 1' 2 3
  1. Liefern von Informationen über gesetzte Signalbehandlung
trap
  1. Zurücksetzen der Signalbehandlung
trap 2 3
  1. Import von Signalen
trap : 2 3
trap  2 3
  1. Demonstriert die Funktion trap zum Abfangen von Signalen
#!/bin/bash
trap 'echo trap ausgelöst' 2
i=0
while [ $i -lt 5 ]
do
   echo "Bitte nicht stören!"
   sleep 2
   i=`expr $i + 1`
done

Bemerkung: Die Signalbehandlung selbst wird nicht an Kindprozesse weitervererbt. Das Ignorieren von Signalen hingegen wird weitervererbt.

Weitere Möglichkeiten der Bash-Shell

Alias-Namen

Mit dem Befehl alias bzw. unalias können zusätzliche Namen für Befehle Befehle vergeben bzw. entfernt werden. Dazu wird ein Name und eine zugehörige Zeichenkette vergeben.

Beides wird in einer Liste abgelegt; bei der Interpretation einer Zeile wird der Aliasname durch die Zeichenkette ersetzt.

Beispiele:

alias			Die bereits definierten Aliasnamen werden aufgelistet.
alias ll='ls -l''	Definition eines neuen Aliasnamens.
alias -x w='who' 	Definieren und Exportieren eines Alias.
unalias ll		Entfernen eines Alias aus der Liste.

Bemerkungen: Am Anfang eines Aliasnamens sind keine Metazeichen erlaubt. Kommt derselbe Name innerhalb der zugewiesenen Zeichenkette noch einmal vor, so wird er nicht ersetzt.

Bearbeiten von Farben

Um die Farben in der Shell zu ändern, müssen wir bestimmte Zeichenfolgen senden. Die Zeichenkette \033\13301;31m würde z.B. alles Weitere in Rot ausgeben.

Format: \033\133;m

Diese Methode kann allerdings beim Setzen der Variable $PS1, die den Prompt kontrolliert, dazu führen, dass der Zeilenumbruch falsch berechnet wird. Deshalb ist in diesem Fall der Einschluss in \[ \] erforderlich.

Format: \[\033\133;m\]

Um mit dem Farbigen aufzuhören und wieder normal zu schreiben sendet man einfach folgende Zeichenfolge:

\033\1330m

Textdekorationen:

00 - Schmaldruck
01 - Keine
02 - dunkle Version der Farbe
04 - Unterstreichen
05 - Invertieren

Farben:

30 - Schwarz
31 - Rot
32 - Grün
33 - Gelb
34 - Blau
35 - Lila
36 - Cyan
37 - Grau

Hintergründe färben:

40 - Schwarz
41 - Rot
42 - Grün
43 - Gelb
44 - Blau
45 - Lila
46 - Cyan
47 - Grau

Austesten wie es dann genau aussieht kann man das mit den folgenden Befehlen:

for i in `seq 40 47`;do echo -e "Farbnummer:\033\13301;"$i"m $i \033\01330m";done
for i in `seq 30 37`;do echo -e "Farbnummer:\033\13301;"$i"m $i \033\01330m";done

getopts

bash specials

[[ is bash's improvement to the [ command. It has several enhancements that make it a better choice if you write scripts that target bash. My favorites are:

    It is a syntactical feature of the shell, so it has some special behavior that [ doesn't have. You no longer have to quote variables like mad because [[ handles empty strings and strings with whitespace more intuitively. For example, with [ you have to write

    if [ -f "$FILE" ]

    to correctly handle empty strings or file names with spaces in them. With [[ the quotes are unnecessary:

    if -f $FILE 

    Because it is a syntactical feature, it lets you use && and || operators for boolean tests and < and > for string comparisons. [ cannot do this because it is a regular command and &&, ||, <, and > are not passed to regular commands as command-line arguments.

    It has a wonderful =~ operator for doing regular expression matches. With [ you might write

    if [ "$ANSWER" = y -o "$ANSWER" = yes ]

    With [[ you can write this as

    if $ANSWER =~ ^y(es)?$ 

    It even lets you access the captured groups which it stores in BASH_REMATCH. For instance, ${BASH_REMATCH[1]} would be "es" if you typed a full "yes" above.

    You get pattern matching aka globbing for free. Maybe you're less strict about how to type yes. Maybe you're okay if the user types y-anything. Got you covered:

    if $ANSWER = y*