<!--#include file="head.html" -->

<p class=title>
<a href=1.109.html>
<img src=../images/lpi-pengu.png border=0 alt="Zurück">
</a>
<h1>1.109.1</h1>
<h1>Anpassung und Verwendung der Shell-Umgebung</h1>
<p><hr><p>
<b>Beschreibung:</b>
Prüfungskandidaten sollten in der Lage sein, Shell-Umgebungen auf die
Bedürfnisse der Benutzer hin anzupassen. Dieses Lernziel beinhaltet das
Setzen von Umgebungsvariablen (z.B. PATH) beim Login oder beim Aufruf einer
neuen Shell. Ebenfalls enthalten ist das Schreiben von Bash-Funktionen für
oft benutzte Kommandoabfolgen. 
<p>
Die wichtigsten Dateien, Bezeichnungen und Anwendungen: 
<p><ul>
<li><tt>~/.bash_profile</tt>
<li><tt>~/.bash_login</tt>
<li><tt>~/.profile</tt>
<li><tt>~/.bashrc</tt>
<li><tt>~/.bash_logout</tt>
<li><tt>~/.inputrc</tt>
<li><b>function</b> (eingebautes Bash-Kommando)
<li><b>export</b>
<li><b>env</b>
<li><b>set</b> (eingebautes Bash-Kommando)
<li><b>unset</b> (eingebautes Bash-Kommando) 
</ul>
<p><hr><p>

<h3>Login-Shell versus NoLogin-Shell</h3>
Unix unterscheidet beim Aufruf einer Shell, ob es sich um eine Login-Shell
handelt, oder um eine No-Login-Shell. Der Unterschied ist einfach: Die
Login-Shell ist die Shell, die beim Login vom login-Programm gestartet
wurde. Jede weitere Shell, die von der Login-Shell aufgerufen wird ist eine
No-Login-Shell.
<p>
Der Grund für diese Unterscheidung liegt in der Frage der
Konfigurationsdateien, die später noch genauer dargestellt werden.
Zunächst nur so viel: Die Login-Shell muß konfiguriert werden, sie erhält
beim Start viele Variablen, die sie an spätere Shells weitervererbt.
<p>
Eine Shell, die wiederum von der Loginshell aufgerufen wird hat diese
Konfiguration nicht nötig. Durch die Tatsache, daß Variablen an aufgerufene
Shells weitergegeben werden können, erledigt sich das von selbst. Außerdem
wäre es eine sehr zeitaufwendige Methode, wenn jede Subshell jedesmal alle
Konfigurationsdateien abarbeiten müsste.
<p>
Die Frage, die sich grundsätzlich stellt ist ja auch die, wozu eine Shell
denn überhaupt eine Subshell aufruft. Das kommt sehr viel häufiger vor, als
auf den ersten Blick ersichtlich. Jedesmal, wenn die Shell ein Script
abarbeitet, wird dazu von ihr eine Subshell aufgerufen. Wenn bei jedem
Scriptaufruf dann alle Konfigurationsdateien abgearbeitet werden würden,
wäre das eine echte Zeitfrage.
<p>
Auch in der graphischen Benutzeroberflache, die ja oft aus einer Shell
heraus gestartet wird (startx) erscheinen wieder mehrere xterm-Fenster, in
denen jeweils eine Shell läuft. All diese Shells sind keine Login-Shells. 
<p>
Eine weitere Unterteilung der No-Login-Shells ist die Frage, ob es sich om
eine interaktive Shell handelt, oder nicht. Diese Frage ist aber
unerheblich, wenn es um die Konfiguration geht.
<p>
<h3>Die Geltungsbereiche von Shell-Variablen</h3>
Die Shellvariablen und Befehle wie <b>set, unset</b> und <a
href=../manpages/env.html>env</a> wurden schon im <a
href=../lpi101/1.103.1.html>Abschnitt 1.103.1</a> der Vorbereitung auf die
LPI101 Prüfung besprochen. Hier geht es jetzt um die Frage des
Geltungsbereichs von Variablen. 
<p>
Wenn innerhalb einer Shell eine Variable definiert wird, und danach eine
zweite Shell aufgerufen wird, so ist die Variable innerhalb der zweiten
Shell nicht automatisch gültig. Das läßt sich einfach ausprobieren, indem
die folgenden Befehle eingegeben werden:
<pre class=shell>
  $ Testvariable=Hallo
  $ echo $Testvariable
  Hallo
  $ bash
  $ echo $Testvariable

  $ exit
  $ echo $Testvariable
  Hallo
</pre>
Wir haben also zunächst eine Variable <tt>Testvariable</tt> mit dem Wert
<tt>Hallo</tt>
angelegt. Im zweiten Schritt geben wir den Inhalt der Variable aus, das
funktioniert erwartungsgemäß. Jetzt starten wir eine zweite Shell mit dem
Aufruf von <b>bash</b>. Der erneute Versuch, sich den Inhalt der Variablen
anzusehen schlägt fehl. Erst wenn wir die zweite Shell mit <tt>exit</tt> 
wieder beenden, funktioniert die Variablenausgabe wieder. 
<p>
Damit eine Shell eine Variable an ihre Subshells weitergibt, muß die
Variable exportiert werden. Das geschieht mit dem Shellbefehl <b>export</b>. 
Es gibt zwei Formen der Anwendungen, entweder wird eine Variable zuerst
definiert und dann exportiert, oder die Variablendefinition geschieht gleich
zusammen mit <tt>export</tt>: 
<pre class=shell>
  Variable1=Versuch
  export Variable1
  export Variable2="Noch ein Versuch"
</pre>
Eine exportierte Variable wird grundsätzlich an alle folgenden Subshells
weitergegeben und ist dort verfügbar. Selbst wenn die Subshell wiederum eine
Shell aufruft, ist die Variable auch innerhalb dieser dritten Shell gültig. 
<p>
Wird allerdings die Variable dann in einer Subshell verändert, so bezieht
sich diese Veränderung nur auf die Subshell. Innerhalb der aufrufenden Shell
behält die Variable ihren alten Wert. Das beweist, daß es sich eben nicht um
die selbe Variable handelt, sondern daß eine exportierte Variable in einer
Subshell nochmal extra angelegt wird. 
<pre class=shell>
  $ export name=hans
  $ echo $name
  hans
  $ bash
  $ echo $name
  hans
  $ name=otto
  $ echo $name
  otto
  $ exit
  $ echo $name
  hans
</pre>
Wie schon bei der Darstellung des Unterschiedes zwischen LoginShell und
NoLoginShell gezeigt, wird jedesmal eine Subshell aufgerufen, wenn ein
Shellscript abgearbeitet werden soll. Wenn in diesem Script Variablen
definiert werden, dann gelten diese Variablen natürlich nur innerhalb der
Subshell. Die aufrufende Shell bekommt von diesen Variablen überhaupt nichts
mit.
<p>
Das ist aber manchmal sehr unpraktisch. Wenn wir etwa ein Script schreiben,
um verschiedene benötigte Shellvariablen anzulegen, dann hilft uns das
herzlich wenig, denn jedesmal wenn wir das Script aufrufen werden diese
Variablen zwar in der Subshell, die das Script ausführt erzeugt - aber
jedesmal, wenn das Script beendet wurde (und damit auch die Subshell) sind
die Variablen nicht mehr gültig.
<p>
Damit es trotzdem möglich ist, ein Script zu verwenden um Variablen zu
definieren, gibt es die Möglichkeit die Shell dazu zu zwingen, keine
Subshell zur Abarbeitung des Scripts aufzurufen. Das geschieht einfach durch
das Voranstellen eines Punktes vor dem Scriptaufruf. Selbstverständlich muß
zwischen dem Punkt und dem Scriptnamen dabei ein Leerzeichen stehen.
<p>
Dadurch wird die Shell gezwungen, das Script selbst abzuarbeiten. Die
Variablen, die innerhalb dieses Scripts definiert wurden, sind jetzt auch
nach der Abarbeitung des Scriptes noch gültig.
<p>
Das klingt schön, hat aber einen bedeutenden Haken. Wenn innerhalb des
Scrips ein exit vorkommt, normalerweise dazu benutzt, um das Script zu
beenden, wird ja die Shell, die das Script ausführt, beendet. Wurde das
Script jetzt mit vorgestelltem Punkt aufgerufen, also von unserer Loginshell
selbst, dann wird das exit die LoginShell beenden! Wir müßten uns also
erneut einloggen.
<p>
Scripts, die mit führendem Punkt aufgerufen werden sollen, sollten daher auf
keinen Fall - auch nicht zur Fehlerbehandlung - ein exit benutzen. 
<p>
<h3>Alias und Funktion in der interaktiven Shell</h3>
Neben den Variablen gibt es noch zwei weitere Formen, die innerhalb einer
Shell definiert werden können. Das Alias und die Funktion. 
<p>
<h4>Alias</h4>
Ein Alias ist sozusagen ein Mechanismus, der der Shell klar macht, daß ein
bestimmter Name eine bestimmte Bedeutung hat. Jedesmal, wenn die Shell auf
einen Befehl trifft, überprüft sie zuerst, ob es sich dabei um einen Alias
handelt, erst wenn es klar ist, daß es kein Alias ist, wird der Unix-Befehl
gesucht. Das heißt, daß damit die Bedeutung von Unix-Befehlen überlagert
werden kann. Ein Alias wird nur ein einziges Mal aufgelöst, so daß es
möglich ist, bestehende Unix-Befehle umzudefinieren.
<p>
<b>Beispiele:</b>
<pre class=shell>
  alias cp=cp -i
</pre>
Damit wird der Befehl cp grundsätzlich als cp -i ausgeführt, das heißt, er
frägt vor dem Überschreiben einer Zieldatei nach, ob das in Ordnung ist. 
<pre class=shell>
  alias ...=cd ..;cd..
</pre>
Der Befehl <tt>...</tt> ist ein Alias auf den Befehl <tt>cd ..;cd ..</tt>, 
also zweimal ins nächsthöhere Verzeichnis zu wechseln. 
<pre class=shell>
  alias werbinich=echo $LOGNAME ($UID)
</pre>
Der Befehl <tt>werbinich</tt> gibt in einer Zeile den Usernamen und die UserID des
Users aus, der ihn ausführt. 
<p>
Um Aliase wieder loszuwerden gibt es den Befehl <b>unalias</b> mit dem ein Alias
wieder gelöscht werden kann. 
<p>
Zur Gültigkeit von Aliasen gilt genau das selbe, wie zur Gültigkeit von
Shellvariablen. Wenn ein Alias auch innerhalb einer Subshell gelten soll, so
muß er wie eine Variable exportiert werden. Die meisten Aliase werden in den
Konfigurationsdateien der Shell definiert und exportiert. 

<p>
<h4>Funktionen</h4>
Der Alias-Mechanismus erlaubt eine vielseitige Anwendung auch kombinierter
Befehle, die unter einem neuen Namen zusammengefasst werden. Er hat aber
zwei wesentliche Einschränkungen. Innerhalb des Alias ist es nicht möglich,
auf einzelne Parameter des Aliases zuzugreifen, die ihm mitgegeben wurden.
Es ist ja tatsächlich ein Textersatz, der hier vorgenommen wird.
<p>
Der zweite Nachteil des Aliases ist, daß seine Interpretation nur einmal
stattfindet, dadurch kann er nicht rekursiv aufgerufen werden. 
<p>
Beide Nachteile (die in Wahrheit keine Nachteile sind, sondern nur
Unterschiede zur Funktion) werden von den Shellfunktionen abgeschafft. Es
handelt sich hier um einen echten Funktionsaufruf, der die mitgegebenen
Parameter bearbeiten kann und der rekursiv laufen kann, das heißt, die
Funktion kann sich selber aufrufen. 
<p>
Shellfunktionen sind kleine Unterprogramme, die einen eigenen Namen haben.
Sie können sowohl im Script, als auch in der interaktiven Shell verwendet
werden. 
<p>
Die prinzipielle Aufgabe von Funktionen in Programmiersprachen ist, immer
wiederkehrende Aufgaben oder logisch zusammenhängende Teile eines Programms
zu separieren und als extra Funktion zu lösen. Dabei können einer Funktion,
genauso wie dem Script selbst, Parameter übergeben werden; dazu kann eine
Funktion einen eigenen (numerischen) Rückgabewert liefern.
<p>
Der prinzipielle Aufbau einer Shellfunktion ist:
<pre>
  function Funktionsname()
  {
    Kommando1
    ... 
  }
</pre>
Das Schlüsselwort <tt>function</tt> kann auch weggelassen werden, weil die Shell eine
Funktion an den Klammern () am Ende des Funktionsnamens erkennt. 
<p>
Parameter, die einer Funktion mitgegeben werden, werden nicht in den
Klammern im Funktionskopf vordefiniert, wie in anderen Programmiersprachen.
Innerhalb der Funktion gelten für die übergebenen Parameter die gleichen
Regeln, wie beim Script selbst. <tt>$1</tt> bezeichnet den ersten übergebenen
Parameter, <tt>$2</tt> den zweiten usw. Auch <tt>$*, $@</tt> und <tt>$#</tt> 
beziehen sich auf die
Parameter der Funktion und nicht mehr auf die des Scripts. Allein der
Parameter <tt>$0</tt> behält den Namen des Scripts und nicht den der Funktion. 
<p>
Genauer gesagt gelten all die genannten Variablen (<tt>$1 - $9, $#, $*,
$@</tt>)
innerhalb einer Funktion als lokale Parameter. 
<p>
Funktionen können direkt eingegeben werden, das ist aber eher selten. Meist
werden sie in den Konfigurationsdateien erstellt und exportiert. Sie müssen
wie Variablen und Aliase exportiert werden, damit sie auch in späteren
Shells gültig sind.
<p>
Wollen wir z.B. eine Funktion schreiben, die beim Verzeichniswechsel gleich
den Inhalt des Verzeichnisses anzeigt, in das gewechselt wurde, so können
wir das folgendermaßen erledigen: 
<p>
<pre class=shell>
  function lscd()
  {
    cd $*
    ls
  }
</pre>
In einem Alias wäre das nicht möglich gewesen, weil wir ja den
Aufrufparameter ($*) nicht gehabt hätten. Was aber, wenn wir diese Funktion
nicht <tt>lscd</tt> sondern einfach nur <tt>cd</tt> genannt hätten? 
<p>
Das hätte ziemlich schnell dazu geführt, daß der Computer keinen
Arbeitsspeicher mehr übrig gehabt hätte. Die Funktion hätte sich nämlich
permanent selbst aufgerufen. Ohne eine vernünftige Abbruchbedingung hätte
das dazu geführt, daß es zu einer Endlosschleife kommt, die in jedem
Durchlauf Speicher anfordert. Es wäre also ein sogenannter rekursiver Aufruf
gewesen.
<p>
Damit zumindestens interne Befehle der Shell wie <tt>cd</tt> überlagert werden
können, bietet die Shell das reservierte Wort <tt>builtin</tt> an, das anzeigt, daß
in jedem Fall der interne Befehl und nicht die Funktion aufgerufen werden
soll. Um unser Beispiel also arbeitsfähig zu machen müßten wir schreiben:
<pre class=shell>
function cd()
{
  builtin cd $*
  ls
}
</pre>
<p>
<h3>Die Konfigurationsdateien der bash</h3>
Bei den Konfigurationsdateien für die Shell handelt es sich um einfache
Shellscripts, deren Ausführungsrecht nicht gesetzt sein muß. Die Shell führt
diese Scripts selbst aus, ohne eine Subshell aufzurufen. Die Variablen,
Aliase und Funktionen, die in diesen Scripts definiert wurden, gelten also
innerhalb der aufrufenden Shell. In der Regel werden diese Variablen, Aliase und
Funktionen exportiert, damit sie auch in kommenden Subshells gültig sind.
<p>
Natürlich können auch andere Programme aus den Konfigurationsscripts heraus
gestartet werden, typisch ist z.B. der Aufruf von <a
href=../manpages/umask.html>umask</a>, um festzulegen, mit
welchen Zugriffsrechten Dateien versehen werden sollen, die neu angelegt
werden. Auch Tastaturdefinitionen o.ä. können hier eingestellt werden.
<p>
Grundsätzlich unterscheidet die Shell bei Konfigurationsdateien, zwischen
Login-Shells und Nicht-Login-Shells. Nur bei Login-Shells werden alle
Konfigurationsdateien abgearbeitet. Bei allen anderen Shells wird nur
optional eine Datei abgearbeitet, die nicht von der Login-Shell benutzt
wird. Konkret werden die folgenden Dateien in der angegebenen Reihenfolge
und Abhängigkeit von der Login-Shell abgearbeitet: 
<ul>
Wenn die Datei <tt>/etc/profile</tt> existiert, wird sie abgearbeitet
<p>
Wenn im Heimatverzeichnis des Users die Datei <tt>.bash_profile</tt> existiert wird
sie abgearbeitet.
<p><ul>
Wenn diese Datei nicht existiert wird die Datei <tt>.bash_login</tt> im
Heimatverzeichnis des Users gesucht und falls gefunden, abgearbeitet.
<p><ul>
Wenn auch diese Datei nicht existiert, so wird im Heimatverzeichnis des
Users die Datei <tt>.profile</tt> gesucht und falls gefunden abgearbeitet. 
</ul>
</ul>
</ul>
Diese Konstruktion ermöglicht es, sowohl die systemweiten Einstellungen
vorzunehmen, als auch jedem User die Freiheit zu lassen, selbst
Einstellungen vorzunehmen. Die <tt>/etc/profile</tt>-Datei kann nur vom
Systemverwalter verändert werden, alle anderen Dateien befinden sich ja in
den Heimatverzeichnissen der einzelnen User. Sie sind meist frei editierbar.
<p>
Eine einzige Datei wird von der Nicht-Login-Shell abgearbeitet, falls sie
existiert - <tt>.bashrc</tt> im Heimatverzeichnis jedes Users. 
<p>
Es gibt auch noch eine Datei, die beim Logout - also dem Beenden der
LoginShell - abgearbeitet wird, sofern sie existiert. Sie heißt
<tt>.bash_logout</tt>
und befindet sich auch im Heimatverzeichnis der einzelnen User. Damit ist es
z.B. möglich, daß temporäre Dateien gelöscht werden oder ähnliche
Aufräumarbeiten erledigt werden. 
<p>
Die Bourne Again Shell benutzt eine Library, die für den Umgang mit
Eingabezeilen gemacht ist. Diese Library heiß Readline und ist ihrerseits
konfigurierbar. Ihre Einstellungen nimmt sie in der Datei <tt>.inputrc</tt>
im Homeverzeichnis jedes Users vor. In dieser Datei können Tastenbindungen
vorgenommen werden, um die Standard-Tastenbelegung für die Eingabezeile zu
verändern.





<!--#include file="foot.html" -->
