Skip to content

9 Mehr über die Shell

Lernziele

  • Shell und Umgebungsvariable kennenlernen

Vorkenntnisse

  • Shell-Grundkenntnisse (Kapitel 4)

  • Dateiverwaltung und einfache Filterkommandos (Kapitel 6, Kapitel 8)

  • Umgang mit einem Texteditor (Kapitel 3)

9.1 sleep, echo und date

Bevor wir uns mit der eigentlichen Shell befassen, möchten wir Ihnen noch einige weitere Materialien für Experimente bereitstellen. Dazu erklären wir Ihnen hier einige ganz einfache Kommandos:

  • sleep

    Dieses Kommando tut für die angegebene Anzahl von Sekunden gar nichts. Sie können es verwenden, wenn Sie möchten, dass Ihre Shell eine „Kunstpause” einlegt:

    bash $ sleep 10 # Ca. 10 Sekunden lang passiert nichts $ _

  • echo

    Das Kommando „echo” gibt seine Argumente durch Leerzeichen getrennt aus (sonst nichts). Es ist dennoch interessant und nützlich, da die Shell vorher Variablenbezüge (siehe Abschnitt 9.2) und Ähnliches ersetzt:

    bash $ w=Welt $ echo Hallo $w Hallo Welt $ echo Hallo ${w}enbummler Hallo Weltenbummler

    (Das zweite echo zeigt, wie Sie vorgehen können, wenn Sie direkt etwas an den Ausgabewert einer Variable anhängen möchten.)

    💡 Wenn Sie echo mit der Option -n aufrufen, schreibt es am Ende seiner Ausgabe keinen Zeilentrenner.

    bash $ echo -n Hallo Hallo$

  • date

    Das Kommando date (engl. für „Datum”) zeigt das aktuelle Datum und die Uhrzeit an. Sie können in weiten Grenzen bestimmen, wie diese Ausgabe aussehen soll. Rufen Sie dazu „date --help” auf oder lesen Sie mit „man date” die Online-Dokumentation.

    💡 (Beim zweiten Durcharbeiten dieses Kapitels:) Insbesondere kann date als Weltzeituhr verwendet werden, wenn Sie zuvor die Umgebungsvariable TZ auf den Namen einer Zeitzone oder einer wichtigen Stadt (typischerweise der Hauptstadt) setzen.

    bash $ date Thu Oct 5 14:26:07 CEST 2006 $ export TZ=Asia/Tokyo $ date Tue Oct 5 21:26:19 JST 2006 $ unset TZ

    Die gültigen Zeitzonen und Städtenamen können Sie herausfinden, indem Sie im Verzeichnis /usr/share/zoneinfo stöbern.

    Während jeder Anwender die Systemzeit abfragen darf, ist es nur dem Systemadministrator root erlaubt, die Systemzeit mit dem Befehl date und dem Argument MMTThhmm zu verändern. Dabei stehen MM für Monat, TT für Tag, hh für Stunde und mm für Minute. Optional können zusätzlich zwei Stellen für die Jahreszahl (plus gegebenenfalls zwei Stellen für das Jahrhundert) sowie die Sekunden (mit einem Punkt davor) angegeben werden. Dies dürfte jedoch nur in den seltensten Fällen notwendig sein.

    bash $ date Thu Oct 5 14:28:13 CEST 2006 $ date 08181715 date: cannot set date: Operation not permitted Fri Aug 18 17:15:00 CEST 2006

    💡 Mit dem Befehl date wird lediglich die interne Zeit des Linux-Systems geändert. Diese wird nicht zwangsläufig in die CMOS-Uhr auf der Hauptplatine des Rechners übertragen. Daher kann es notwendig sein, sie mit einem speziellen Kommando zu ändern. Viele Distributionen erledigen dies automatisch beim Herunterfahren des Systems.

Übungen

✏️ 9.1 [!3] Angenommen, es ist jetzt der 22. Oktober 2003, 12:34 Uhr und 56 Sekunden. Studieren Sie die Dokumentation von „date” und geben Sie die Formatierungsanweisungen an, mit denen Sie die folgenden Ausgaben erreichen können:

  1. 22-10-2003

  2. 03-294 (KW43) (Zweistellige Jahreszahl, fortlaufende Nummer des Tags im Jahr, Kalenderwoche)

  3. 12h34m56s

✏️ 9.2 [!2] Wie spät ist es aktuell in Los Angeles?

9.2 Shell-Variable und die Umgebung

Die Bash verfügt – wie die meisten gängigen Shells – über Eigenschaften, die sonst in Programmiersprachen zu finden sind. So ist es beispielsweise möglich, Text oder numerische Werte in Variablen abzulegen und später wieder hervorzuholen. Variablen steuern auch verschiedene Aspekte der Funktionsweise der Shell selbst.

In der Shell wird eine Variable durch ein Kommando wie „bla=fasel” gesetzt. Mit dem Kommando „Variable setzen” wird die Variable „bla” auf den Wert „fasel” gesetzt. Achten Sie darauf, dass vor und hinter dem Gleichheitszeichen keine Leerzeichen stehen! Den Wert der Variablen können Sie verwenden, indem Sie den Variablennamen mit einem Dollarzeichen voranstellen:

$ bla=fasel
$ echo bla
bla
$ echo $bla
fasel

(Achten Sie auf den Unterschied.)

Wir unterscheiden zwischen Umgebungs- und Shellvariablen. Shellvariablen sind nur in der betreffenden Shell sichtbar. Umgebungsvariablen hingegen werden beim Starten eines externen Kommandos an den Kindprozess weitergegeben und können auch dort benutzt werden. (Der Kindprozess muss nicht unbedingt eine Shell sein, denn jeder Linux-Prozess hat Umgebungsvariablen.) Alle Umgebungsvariablen einer Shell sind gleichzeitig auch Shellvariablen, aber umgekehrt ist es nicht so.

Mit dem Befehl export können Sie eine existierende Shellvariable zur Umgebungsvariable erklären.

$ bla=fasel bla ist jetzt Shellvariable
$ export bla bla ist jetzt Umgebungsvariable

Tabelle 9.1: Wichtige Variable der Shell

Variable Bedeutung
PWD Name des aktuellen Verzeichnisses PS1 enthält die Zeichenkette, die am Beginn jeder Eingabezeile angezeigt wird (den Prompt)
UID enthält die Benutzerkennung
HOME Pfad des Heimatverzeichnisses des Benutzers PATH Liste von Verzeichnissen, die von der Shell automatisch als Suchpfade für Befehle verwendet werden
LOGNAME enthält den Benutzernamen

Alternativ können Sie eine neue Variable auch direkt als Shell- und Umgebungsvariable definieren:

$ export bla=fasel

Das Kommando „export“ funktioniert auch für mehrere Variablen auf einmal.

$ export bla blubb
$ export bla=fasel blubb=bli

Sie können sich die Umgebungsvariablen mit dem Befehl export (ohne Parameter) anzeigen lassen. Auch der Befehl „env” (ebenfalls ohne Parameter) zeigt die aktuelle Umgebung an. Mit dem Befehl set können Sie sich alle Shellvariablen (inklusive derjenigen, die auch in der Umgebung vorhanden sind) anzeigen lassen. Die gebräuchlichsten Variablen und ihre Zuordnung finden Sie in Tabelle 9.1.

💡 Der Befehl „set” kann noch viele weitere, fremdartige und wundervolle Dinge. Sie werden ihn in der Schulungsunterlage „Linux für Fortgeschrittene” der Linup-Front-Schulung wieder begegnen, wenn es um Shellprogrammierung geht.

💡 env ist eigentlich auch dafür gedacht, die Prozessumgebung zu manipulieren und nicht nur, sie anzuzeigen. Betrachten Sie das folgende Beispiel:

$ env bla=fasel bash Starte Kind-Shell mit bla
$ echo $bla
fasel
$ exit Zurück in die Eltern-Shell
$ echo $bla
Nicht definiert
$ _

💡 In der Bash (und verwandten Systemen) brauchen Sie env nicht wirklich. Sie können Kommandos mit einer erweiterten Umgebung starten. Das geht auch einfacher:

$ bla=fasel bash

Allerdings können Sie mit env auch Variablen temporär aus der Umgebung entfernen.

Wenn Sie eine Shellvariable nicht mehr benötigen, können Sie sie mit dem Befehl unset löschen. Damit verschwindet sie auch aus der Prozessumgebung. Wenn Sie eine Variable aus der Umgebung entfernen, aber als Shellvariable beibehalten möchten, verwenden Sie „export -n“:

$ export bla=fasel bla ist Umgebungsvariable
$ export -n bla bla ist (nur) Shellvariable
$ unset bla bla ist ganz weg

Übungen

✏️ 9.3 [!2] Überzeugen Sie sich davon, dass die Übergabe (oder Nichtübergabe) von Umgebungs- und Shellvariablen an Kindprozesse wie behauptet funktioniert, indem Sie die folgende Kommandosequenz ausführen:

$ bla=fasel bla ist Shellvariable
$ bash Neue Shell (Kindprozess)
$ echo $bla
bla ist nicht definiert
$ exit Zurück in die Eltern-Shell
$ export bla bla ist Umgebungsvariable
$ bash Neue Shell (Kindprozess)
$ echo $bla
fasel Umgebungsvariable wurde vererbt
$ exit Zurück in die Eltern-Shell

✏️ 9.4 [!2] Was passiert, wenn Sie eine Umgebungsvariable im Kindprozess ändern? Betrachten Sie dazu die folgende Kommandosequenz:

$ export bla=fasel # bla ist Umgebungsvariable
$ bash # Neue Shell (Kindprozess)
$ echo $bla # fasel Umgebungsvariable wurde vererbt
$ bla=blubb # Neuer Wert
$ exit # Zurück in die Eltern-Shell
$ echo $bla # Was kommt hier heraus??

9.3 Arten von Kommandos – die zweite

Eine Anwendung von Shellvariablen ist, wie erwähnt, die Steuerung der Shell. Hierzu noch ein Beispiel: Wie in Kapitel 4 beschrieben, unterscheidet die Shell zwischen internen und externen Kommandos. Externe Kommandos entsprechen ausführbaren Programmen, die die Shell in den Verzeichnissen sucht, die in der Umgebungsvariablen PATH definiert sind. Ein typischer Wert für PATH ist:

$ echo $PATH
/home/hugo/bin:/usr/local/bin:/usr/bin:/bin:/usr/games

Die einzelnen Verzeichnisse werden in der Liste durch Doppelpunkte getrennt. Die Liste im Beispiel besteht somit aus fünf Verzeichnissen. Wenn Sie ein Kommando wie

$ ls

eingeben, weiß die Shell, dass es sich nicht um ein internes Kommando handelt (sie kennt ihre internen Kommandos), und beginnt deshalb, die Verzeichnisse in PATH abzusuchen, beginnend am linken Ende. Konkret prüft sie, ob die folgenden Dateien existieren:

/home/hugo/bin/ls Nein …
/usr/local/bin/ls Immer noch nicht …
/usr/bin/ls Nach wie vor nicht …
/bin/ls Hah, Treffer!

Das Verzeichnis /usr/games wird nicht mehr betrachtet.

Das heißt, zur Ausführung des Kommandos ls wird die Datei /bin/ls herangezogen.

💡 Diese Suche ist natürlich ein relativ aufwendiger Prozess. Darum baut die Shell für die Zukunft vor: Hat sie die Datei /bin/ls einmal als Implementierung des Kommandos ls ausgemacht, merkt sie sich diese Zuordnung bis auf weiteres. Dieser Vorgang wird als hashing bezeichnet. Dass er stattgefunden hat, können Sie sehen, wenn Sie das Kommando type auf unser Kommando ls anwenden:

$ type ls
ls is hashed (/bin/ls)

💡 Mit dem Kommando „hash“ erfahren Sie, welche Befehle Ihre Bash bereits „gehasht“ hat und wie oft sie seitdem aufgerufen wurden. Mit „hash -r” können Sie das komplette Hashing-Gedächtnis der Shell löschen. Es gibt noch einige andere Optionen, die Sie in der Bash-Dokumentation nachschlagen oder mit dem Befehl „help hash” herausfinden können.

💡 Die Variable PATH muss prinzipiell keine Umgebungsvariable sein – für die aktuelle Shell funktioniert sie auch als Shellvariable (siehe Übung 9.5). Es ist allerdings bequemer, sie als Umgebungsvariable zu definieren, damit auch die Kindprozesse der Shell (oftmals ebenfalls Shells) den gewünschten Wert verwenden.

Wenn Sie wissen möchten, welches Programm die Shell für ein externes Kommando heranzieht, können Sie das Kommando which verwenden.

$ which grep
/bin/grep

which verwendet dasselbe Verfahren wie die Shell: Es beginnt beim ersten Verzeichnis in PATH und prüft jeweils, ob es in dem betreffenden Verzeichnis eine ausführbare Datei mit dem Namen des gesuchten Kommandos gibt.

💡 which kennt die internen Kommandos der Shell nicht. Selbst wenn which test ausführt und „/usr/bin/test” als Ergebnis liefert, bedeutet das nicht, dass dieses Programm tatsächlich ausgeführt wird, denn interne Kommandos haben Vorrang gegenüber externen. Wenn Sie wissen wollen, was wirklich passiert, müssen Sie das Shell-Kommando „type” benutzen (Abschnitt 4.3.3).

Das Kommando whereis liefert neben ausführbaren Programmen auch Dokumentationsdateien (Manpages), Quellcodedateien und andere interessante Dateien, die mit den angegebenen Kommandos in Zusammenhang stehen. Zum Beispiel:

$ whereis passwd
passwd: /usr/bin/passwd /etc/passwd /etc/passwd.org /usr/share/passwd✄
✁ /usr/share/man/man1/passwd.1.gz /usr/share/man/man1/passwd.1ssl.gz✄
✁ /usr/share/man/man5/passwd.5.gz

Dafür wird ein Verfahren verwendet, das hartcodiert im Programm ist und ansatzweise in whereis(1) erklärt wird.

Übungen

✏️ 9.5 [2] Überzeugen Sie sich davon, dass die Kommandosuche der Shell auch dann funktioniert, wenn PATH keine Umgebungsvariable, sondern „nur” eine Shellvariable ist. Was passiert, wenn Sie PATH komplett entfernen?

✏️ 9.6 [!1] Wie heißen auf Ihrem System die ausführbaren Programme, die zur Bearbeitung der folgenden Kommandos herangezogen werden: fgrep, sort, mount und xterm?

✏️ 9.7 [!1] Wie heißen auf Ihrem System die Dateien, die die Dokumentation zum Kommando „crontab” enthalten?

9.4 Die Shell als komfortables Werkzeug

Da die Shell für viele Linux-Anwender das am häufigsten genutzte Werkzeug ist, haben sich deren Entwickler große Mühe gegeben, sie komfortabel zu gestalten. Im Folgenden zeigen wir Ihnen noch ein paar nützliche Funktionen.

  • Kommandoeditor

    Sie können die Kommandozeile wie mit einem einfachen Texteditor bearbeiten. Sie können den Cursor in der Zeile hin und her bewegen sowie Zeichen beliebig löschen oder hinzufügen, bis Sie die Eingabetaste drücken. Das Verhalten dieses Editors kann übrigens mit „set -o vi” bzw. „set -o emacs” an das der beiden bekanntesten Editoren unter Linux (Kapitel 3) angepasst werden.

  • Kommandoabbruch

    Bei den zahlreichen Linux-Kommandos kann es durchaus vorkommen, dass Sie einen Namen verwechseln oder einen falschen Parameter übergeben. Deshalb können Sie ein Kommando abbrechen, während es läuft. Drücken Sie dazu die Tasten Strg + c.

  • Die »Vorgeschichte«

    Die Shell merkt sich die zuletzt ausgeführten Kommandos als Vorgeschichte, auch History genannt. Mit den Cursortasten "↑" und "↓" können Sie sich in der Liste nach oben und unten bewegen. Wenn Sie ein früheres Kommando finden, das Ihnen gefällt, können Sie es mit der Enter-Taste einfach so, wie es ist, erneut ausführen oder es zunächst abändern. Mit Strg + R können Sie die Liste „inkrementell” durchsuchen: Geben Sie einfach eine Zeichenfolge ein und die Shell zeigt Ihnen das zuletzt ausgeführte Kommando, das diese Zeichenfolge enthält. Je länger Ihre Zeichenfolge ist, desto präziser wird die Suche.

    💡 Beim ordnungsgemäßen Verlassen des Systems wird die Vorgeschichte in der versteckten Datei ~/.bash_history gespeichert und steht nach dem nächsten Anmelden wieder zur Verfügung. (Sie können einen anderen Dateinamen verwenden, indem Sie die Variable „HISTFILE” auf den gewünschten Namen setzen.)

    💡 Eine Konsequenz der Tatsache, dass die Vorgeschichte in einer „gewöhnlichen” Datei abgespeichert wird, ist, dass Sie sie mit einem Texteditor ändern können. (In Kapitel 3 wird erklärt, wie das geht.) Sollten Sie also versehentlich Ihr Kennwort auf der Kommandozeile eintippen, können (und sollten!)

    Sie es mit der Hand aus der Vorgeschichte entfernen – vor allem, wenn Ihr System zu den eher freizügigen gehört, bei denen Heimatverzeichnisse standardmäßig für alle anderen Benutzer lesbar sind.

Tabelle 9.2: Tastaturkürzel innerhalb der Bash

Tastaturkürzel Funktion
↑ bzw. ↓ durch frühere Kommandos blättern
Strg + r Frühere Kommandos durchsuchen
← bzw. → Cursor in Kommandozeile bewegen
Pos1 oder Strg + a Cursor an Zeilenanfang setzen
Ende oder Strg + e Cursor an Zeilenende setzen
⇐ bzw. Entf Zeichen vor bzw. nach Cursor löschen
Strg + t die beiden Zeichen vor/unter Cursor tauschen
Strg + l Bildschirm löschen
Strg + c Kommando abbrechen
Strg + d Eingabe beenden (in der Login-Shell: Abmelden)
  • Autovervollständigung

    Eine große Bequemlichkeit ist die Fähigkeit der Bash, Kommandos bzw. Dateinamen automatisch zu vervollständigen. Wenn Sie die Tab-Taste drücken, vervollständigt die Shell Ihre Eingabe, sofern diese eindeutig identifiziert werden kann. Hierzu werden für das erste Wort des Kommandos alle ausführbaren Programme und für die weiteren Wörter die sich im aktuellen bzw. angegebenen Verzeichnis befindlichen Dateien herangezogen. Existieren hierbei mehrere Dateien, deren Bezeichnungen gleich beginnen, vervollständigt die Shell die Eingabe so weit wie möglich. Durch ein akustisches Signal wird angezeigt, dass der Datei- bzw. Befehlsname noch immer unvollständig ist. Durch erneutes Drücken der Tab-Taste werden die verbleibenden Möglichkeiten aufgelistet.

    Wenn Sie einen (oder einige) Buchstaben eingeben und dann zweimal die Tab-Taste drücken, gibt die Shell eine Liste aller verfügbaren Befehle mit dem entsprechenden Anfangsbuchstaben aus. Dies ist zum Beispiel sehr hilfreich, um sich die Schreibweise selten verwendeter Befehle ins Gedächtnis zurückzurufen. Der gleiche Effekt lässt sich auch mit der Tastenkombination Esc+? erzielen. Soll ein Dateiname vervollständigt werden, kann dies mit Esc/ erfolgen.

    💡 Sie können die Vervollständigung der Shell an bestimmte Programme anpassen. Beispielsweise könnte sie auf der Kommandozeile eines FTP-Programms statt Dateinamen die Namen bereits besuchter Rechner anbieten. Näheres dazu steht in der Bash-Dokumentation.

Tabelle 9.2 gibt eine Übersicht über die in der Bash möglichen Tastaturkürzel.

  • Mehrere Kommandos auf einer Zeile

    Sie können durchaus mehrere Befehle auf derselben Eingabezeile angeben. Trennen Sie diese dazu lediglich mit einem Semikolon:

    bash $ echo Heute ist; date Heute ist Fr 5. Dez 12:12:47 CET 2008

    In diesem Fall wird das zweite Kommando ausgeführt, sobald das erste abgeschlossen ist.

  • Bedingte Ausführung

    Es kann manchmal nützlich sein, die Ausführung des zweiten Kommandos davon abhängig zu machen, ob das erste korrekt ausgeführt wurde. Jeder Unix-Prozess liefert einen Rückgabewert, der angibt, ob er korrekt ausgeführt wurde oder ob Fehler aufgetreten sind. Im ersteren Fall ist der Rückgabewert 0, im letzteren Fall ist er von 0 verschieden.

    💡 Den Rückgabewert eines Kindprozesses Ihrer Shell können Sie herausfinden, indem Sie die Variable $? betrachten.

    bash $ bash # Eine Kind-Shell starten … $ exit 33 # … und gleich wieder beenden exit $ echo $? 33 # Der Wert aus unserem exit oben $ _

    Für das Folgende ist das aber eigentlich egal.

    Wenn Sie das Trennzeichen „&” zwischen zwei Kommandos verwenden (an der Stelle, an der sonst ein Semikolon stünde), wird das zweite Kommando nur dann ausgeführt, wenn das erste erfolgreich beendet wurde. Um Ihnen das zu demonstrieren, benutzen wir die -c-Option der Bash. Damit können Sie der Kind-Shell ein Kommando auf der Kommandozeile übergeben (beeindruckend, oder?).

    bash $ bash -c ""exit 0"" && echo ""Erfolgreich"" Erfolgreich $ bash -c ""exit 33"" && echo ""Erfolgreich"" Nichts 33 ist kein Erfolg!

    Mit dem Trennzeichen „||” wird das zweite Kommando nur dann ausgeführt, wenn das erste nicht erfolgreich beendet wurde.

    bash $ bash -c ""exit 0"" || echo ""Nicht erfolgreich"" $ bash -c ""exit 33"" || echo ""Nicht erfolgreich"" Nicht erfolgreich

Übungen

✏️ 9.8 [3] Was ist das Problem mit dem Kommando „echo 'Hallo'“? (Tipp: Experimentieren Sie mit Kommandos der Form „!-2” oder „!ls”.)

9.5 Kommandos aus einer Datei

Sie können Shell-Kommandos in einer Datei ablegen und diese dann en bloc ausführen. (Wie das Anlegen einer Datei funktioniert, erfahren Sie in Kapitel 3.)

Rufen Sie dazu einfach die Shell auf und übergeben Sie den Namen der Datei als Parameter:

$ bash meine-kommandos

Eine solche Datei bezeichnet man auch als Shellskript. Die Shell bietet umfangreiche Möglichkeiten zur Programmierung, auf die wir an dieser Stelle nicht näher eingehen können. (Die LinupFront-Schulungsunterlage „Linux für Fortgeschrittene” erklärt die Programmierung von Shellskripten sehr ausführlich.)

💡 Sie können sich die vorgesetzte Bash sparen, indem Sie als erste Zeile der Skriptdatei die Anweisung #!/bin/bash einfügen und die Skriptdatei ausführbar machen.

$ chmod +x meine-kommandos

(Mehr über chmod und Zugriffsrechte erfahren Sie in Kapitel 14.) Danach genügt der Aufruf:

$ /meine-kommandos

Wenn Sie ein Shellskript wie oben gezeigt aufrufen – ob mit vorgesetztem „bash” oder als ausführbare Datei –, wird es in einer Sub-Shell ausgeführt. Das ist eine Shell, die ein Kindprozess der aktuellen Shell ist. Das bedeutet, dass Änderungen an Shell oder Umgebungsvariablen die aktuelle Shell nicht beeinflussen. Nehmen wir einmal an, die Datei zuweisung enthält die Zeile:

bla=fasel

Betrachten Sie die folgende Kommandosequenz:

$ bla=blubb
$ bash # zuweisung Enthält bla=fasel
$ echo $bla
blubb # Keine Änderung; Zuweisung war nur in Sub-Shell

In der Regel wird das als Vorteil empfunden, aber hin und wieder wäre es wünschenswert, Kommandos aus einer Datei in der aktuellen Shell auszuführen. Auch das ist möglich: Das Kommando „source” liest die Zeilen einer Datei so ein, als ob Sie sie direkt in die aktuelle Shell eingeben würden. Alle Änderungen von Variablen wirken sich somit auf die aktuelle Shell aus:

$ bla=blubb
$ source # zuweisung Enthält bla=fasel
$ echo $bla
fasel # Variable wurde geändert!

Übrigens gibt es für das Kommando source auch einen anderen Namen, nämlich "". Sie haben richtig gelesen – es handelt sich um einen Punkt! Statt:

$ source zuweisung

funktioniert also auch

$ . zuweisung 

💡 Wie bei externen Kommandos werden auch bei Dateien, die mit source bzw. gelesen werden sollen, die Verzeichnisse durchsucht, die in der Variable PATH angegeben sind.

9.6 Die Shell als Programmiersprache

Shellkommandos aus einer Datei ausführen zu können, ist zweifellos eine gute Sache. Noch besser ist es jedoch, diese Shellkommandos so zu strukturieren, dass sie nicht jedes Mal dasselbe tun müssen, sondern beispielsweise Parameter von der Kommandozeile lesen können. Die Vorteile liegen auf der Hand: Bei oft verwendeten Vorgängen wird lästiges Tippen eingespart und bei selten verwendeten Vorgängen werden Fehler vermieden, die sich einschleichen können, weil man versehentlich einen wichtigen Schritt auslässt. Der Platz reicht hier nicht für eine komplette Erklärung der Shell als Programmiersprache, aber für ein paar kurze Beispiele ist zum Glück Raum.

Die Parameter der Kommandozeile eines Shellskript-Aufrufs stellt die Shell in den Variablen $1, $2, … zur Verfügung. Einzelne Parameter

Betrachten Sie das folgende Beispiel:

$ cat hallo
#!/bin/bash
echo Hallo $1, was machst Du $2?
$ /hallo Hugo heute
Hallo Hugo, was machst Du heute?
$ /hallo Susi morgen
Hallo Susi, was machst Du morgen?

Die Variable $* enthält alle Parameter auf einmal. Die Anzahl der Parameter steht in $#:

$ cat parameter
#!/bin/bash
echo $# Parameter: $*
$ /parameter
0 Parameter:
$ /parameter Hund
1 Parameter: Hund
$ /parameter Hund Katze Maus Baum
4 Parameter: Hund Katze Maus Baum
  • Schleifen

    Mit dem Kommando for können Sie Schleifen konstruieren. Diese Schleifen laufen über eine Liste von (durch Freiplatz getrennten) Wörtern:

    ```bash $ for i in 1 2 3

    do echo Und $i! done Und 1! Und 2! Und 3! ```

    Dabei nimmt die Variable „i” nacheinander jeden der aufgelisteten Werte an. Jedes Mal werden die Kommandos zwischen „do” und „done” ausgeführt.

    Das Ganze macht mehr Spaß, wenn die Wörter aus einer Variablen stammen:

    ```bash $ liste='4 5 6' $ for i in $liste

    do echo Und $i! done Und 4! Und 5! Und 6! ```

    Wenn Sie das »in …« weglassen, läuft die Schleife über die Parameter von der Schleife über Parameter Kommandozeile:

    ```bash $ cat sort-wc

    !/bin/bash

    Sortiere Dateien nach ihrer Zeilenzahl for f

    do echo `wc -l <""$f» Zeilen in $f done | sort -n $ /sort-wc /etc/passwd /etc/fstab /etc/motd ```

    (Das Kommando „wc -l” zählt die Zeilen seiner Standardeingabe oder der übergebenen Dateien.) Beachten Sie, dass Sie die Standardausgabe der Schleife mit einer Pipe an sort weiterleiten können!

  • Fallunterscheidungen

    Sie können die weiter vorne gezeigten Operatoren „&&” und „||” verwenden, um bestimmte Befehle nur unter bestimmten Umständen auszuführen. Das Skript

    ```bash

    !/bin/bash

    grepcp REGEX

    rm -rf backup; mkdir backup for f in *.txt do grep $1 ""$f"" && cp ""$f"" backup done ```

    zum Beispiel wird nur diejenigen Dateien kopieren, deren Name auf „txt” endet und die mindestens eine Zeile enthalten, auf die der reguläre Ausdruck passt, der als Parameter übergeben wurde.

    Für Fallunterscheidungen ist das Kommando test nützlich, da es eine große Auswahl von Bedingungen überprüfen kann. Es liefert den Rückgabewert 0 (Erfolg), wenn die Bedingung zutrifft, sonst einen von Null verschiedenen Rückgabewert (Misserfolg). Betrachten Sie zum Beispiel:

    ```bash

    !/bin/bash

    filetest NAME1 NAME2 .. for name

    do test -d ""$name"" && echo $name: Verzeichnis test -f ""$name"" && echo $name: Datei test -L ""$name"" && echo $name: Symbolisches Link done ```

    Mit diesem Skript können Sie eine Reihe von Dateinamen überprüfen und für jeden Namen ausgeben lassen, ob es sich um einen Verzeichnis-, Datei- oder symbolischen Link-Namen handelt.

    💡 Das Kommando „test” existiert sowohl als eigenständiges Programm in /bin/test als auch als eingebautes Kommando in der Bash und anderen Shells. Die verschiedenen Versionen können, vor allem bei exotischeren Tests, subtil voneinander abweichen. Lesen Sie in diesem Fall in der Dokumentation nach.

    Mit dem if-Kommando können Sie bequem und leserlich mehrere Befehle von einer Fallunterscheidung abhängig machen. Anstelle von „test ...” können Sie auch „[...]” verwenden.

    ```bash

    !/bin/bash

    filetest2 NAME1 NAME2 .. for name

    do if [ -L ""$name"" ] then echo $name: Symbolisches Link elif [ -d ""$name"" ] echo $name: Verzeichnis elif [ -f ""$name"" ] echo $name: Datei else echo $name: Keine Ahnung fi done ```

    Wenn das Kommando nach dem if-Befehl „Erfolg“ meldet (Rückgabewert 0), werden die Kommandos nach then ausgeführt, bis zu einem elif-, else- oder fi-Befehl. Liefert es hingegen „Misserfolg“, wird testhalber als Nächstes das Kommando nach dem nächsten elif ausgeführt und dessen Rückgabewert betrachtet. Die Shell macht entsprechend weiter, bis das passende „fi“ erreicht ist. Dabei werden die Kommandos hinter dem „else“ ausgeführt, wenn keines der „if“-Kommandos Erfolg vermelden konnte. Die elif- und else-Zweige dürfen wegfallen, wenn sie nicht gebraucht werden.

    Mehr Schleifen: Bei der For-Schleife liegt die Anzahl der Schleifendurchläufe von Anfang an fest (die Anzahl der Wörter in der Liste). Oft bekommt man es jedoch mit Situationen zu tun, in denen nicht von vornherein klar ist, wie oft eine Schleife durchlaufen werden soll. Hierfür bietet die Shell die While-Schleife an, die ähnlich wie If ein Kommando ausführt, dessen Erfolg oder Misserfolg darüber entscheidet, wie mit der Schleife verfahren wird. Bei Erfolg werden die „abhängigen” Kommandos ausgeführt, bei Misserfolg wird nach der Schleife im Skript fortgefahren.

    Das folgende Skript liest eine Datei ein, die über die Kommandozeile übergeben wurde und die folgende Form hat:

    text Liebe Tante Frieda:frieda@example.net:den tollen Kaffeewärmer Lieber Onkel Hans:hans@example.com:den schönen Fußball ✁✁✁✁✁

    Und konstruiert aus jeder Zeile eine Dankes-E-Mail, denn Linux ist halt schon sehr nützlich fürs wirkliche Leben.

    ```bash

    !/bin/bash

    birthday FILE IFS=:

    while read anrede adresse geschenk do (echo $anrede! echo """" echo ""Vielen Dank für $geschenk!"" echo ""Ich habe mich sehr darüber gefreut."" echo """" echo ""Viele Grüße,"" echo ""Dein Hugo"") | mail -s ""Vielen Dank!"" $adresse done <$1 ```

    Das Kommando „read” liest die Eingabedatei dabei Zeile für Zeile und teilt jede Zeile an den Doppelpunkten (Variable „IFS”) in die drei Felder „Anrede”, „Adresse” und „Geschenk” auf. Diese stehen dann im Inneren der Schleife als Variable zur Verfügung. Die Eingabeumleitung für die Schleife steht etwas konterintuitiv ganz am Ende.

    💡 Bitte testen Sie dieses Skript nur mit unverfänglichen E-Mail-Adressen!

Übungen

✏️ 9.9 [1] Was ist der Unterschied (bei der Schleifenausführung) zwischen for f; do …; done und for f in $*; do …; done? (Probieren Sie es notfalls aus.)

✏️ 9.10 [2] Warum benutzen wir im Skript sort-wc das Kommando wc -l <$f und nicht wc -l $f

✏️ 9.11 [2] Ändern Sie das grepcp-Skript so, dass es auch die Liste der zu betrachtenden Dateien von der Kommandozeile übernimmt. (Tipp: Das Shell-Kommando shift entfernt den ersten Kommandozeilenparameter aus $ und lässt alle anderen um eine Position aufrücken. Nach einem shift ist das vormalige $2 jetzt $1, $3 ist $2 und so weiter.)

✏️ 9.12 [2] Warum liefert das Skript filetest für ein symbolisches Link die Ausgabe

$ /filetest foo
foo: Datei
foo: Symbolisches Link

(statt nur einfach »foo: Symbolisches Link«)?

9.3 Kommandos in diesem Kapitel

Kommando Beschreibung manpage
. Liest eine Datei mit Shell-Kommandos so ein, als ob sie auf der Kommandozeile eingegeben worden wäre bash(1)
date Gibt Datum und Uhrzeit aus date(1)
env Gibt die Prozessumgebung aus oder startet Programme mit veränderter Umgebung env(1)
export Definiert und verwaltet Umgebungsvariable bash(1)
hash Zeigt und verwaltet ”gesehene“ Kommandos in der bash bash(1)
set Verwaltet Shellvariable bash(1)
source Liest eine Datei mit Shell-Kommandos so ein, als ob sie auf der Kommandozeile eingegeben worden wäre bash(1)
test Wertet logische Ausdrücke auf der Kommandozeile aus test(1), bash(1)
unset Löscht Shell oder Umgebungsvariable bash(1)
whereis Sucht ausführbare Programme, Handbuchseiten und Quellcode zu gegebenen Kommandos whereis(1)
which Sucht Programme in PATH which(1)

9.4 Zusammenfassung

  • Das Kommando „sleep” wartet für die als Parameter angegebene Anzahl von Sekunden.

  • Das Kommando „echo” gibt seine Argumente aus.

  • Mit dem Befehl date lassen sich Datum und Uhrzeit ermitteln.

  • Verschiedene Eigenschaften der Bash unterstützen das interaktive Arbeiten, etwa die Vervollständigung von Kommandos und Dateinamen, das Editieren der Kommandozeile sowie Aliasnamen und Variablen.