3 Kommandozeile

3.1 Grundlegende Konzepte der Kommandozeileninteraktion

Die Kommandozeile ist das primäre Interaktionsmittel mit einer Shell. Um effektiv mit der Shell arbeiten zu können – sei es interaktiv oder beim Schreiben von Skripten – ist ein solides Verständnis der grundlegenden Konzepte unerlässlich. In diesem Abschnitt werden wir die wesentlichen Elemente der Kommandozeileninteraktion kennenlernen und anhand praktischer Beispiele veranschaulichen.

3.1.1 Befehle und ihre Struktur

Ein Befehl ist die grundlegendste Einheit der Interaktion mit der Shell. In seiner einfachsten Form besteht ein Befehl aus dem Namen eines ausführbaren Programms. Die allgemeine Struktur eines Befehls ist:

befehlsname [optionen] [argumente]

Hierbei sind: - befehlsname: Der Name des auszuführenden Programms oder Shell-Builtin-Befehls - optionen: Modifikatoren, die das Verhalten des Befehls steuern (optional) - argumente: Daten oder Ziele, auf die der Befehl angewendet wird (optional)

Betrachten wir ein einfaches Beispiel:

ls -l /home/benutzer

In diesem Beispiel ist: - ls der Befehlsname (listet Verzeichnisinhalte auf) - -l eine Option (für das “lange” Listenformat) - /home/benutzer ein Argument (das Verzeichnis, dessen Inhalt angezeigt werden soll)

Die Shell interpretiert diese Eingabe, findet das ausführbare Programm ls im System, startet es und übergibt die Option und das Argument an das Programm.

3.1.2 Befehle finden und identifizieren

Befehle in Unix/Linux-Systemen können aus verschiedenen Quellen stammen:

  1. Eingebaute Befehle (Builtins): Direkt in die Shell integrierte Befehle wie cd, echo oder pwd.
  2. Externe Programme: Eigenständige ausführbare Dateien, die in Verzeichnissen gespeichert sind, die im PATH aufgeführt sind.
  3. Aliase: Benutzerdefinierte Abkürzungen für längere Befehle.
  4. Funktionen: Von Benutzern definierte Befehlssequenzen.
  5. Schlüsselwörter: Spezielle Worte mit besonderer Bedeutung für die Shell, wie if, for oder while.

Um herauszufinden, welche Art von Befehl verwendet wird, kann der Befehl type genutzt werden:

$ type cd
cd is a shell builtin
$ type ls
ls is aliased to `ls --color=auto'
$ type gcc
gcc is /usr/bin/gcc

Um alle Verzeichnisse zu sehen, in denen die Shell nach ausführbaren Dateien sucht, kann der Inhalt der PATH-Umgebungsvariable angezeigt werden:

$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin

3.1.3 Optionen und Argumente

Optionen (auch Flags oder Schalter genannt) modifizieren das Verhalten eines Befehls. Es gibt zwei gängige Formate für Optionen:

  1. Kurze Optionen: Einzelne Buchstaben mit einem vorangestellten Bindestrich, z.B. -l, -a. Mehrere kurze Optionen können kombiniert werden: -la ist äquivalent zu -l -a.

  2. Lange Optionen: Vollständige Wörter mit zwei vorangestellten Bindestrichen, z.B. --all, --human-readable. Lange Optionen können nicht kombiniert werden und bieten oft eine selbsterklärende Alternative zu kurzen Optionen.

Manche Optionen erfordern zusätzliche Werte, die entweder direkt angehängt oder als nächstes Argument angegeben werden:

# Kurze Option mit Wert
$ grep -n "Muster" datei.txt  # Zeigt Zeilennummern an

# Lange Option mit Wert
$ grep --context=3 "Muster" datei.txt  # Zeigt 3 Zeilen Kontext um den Treffer

Argumente sind die Eingabedaten oder Zielobjekte, auf die der Befehl angewendet wird. Sie folgen in der Regel nach den Optionen (obwohl einige Befehle Argumente und Optionen in unterschiedlicher Reihenfolge akzeptieren).

Ein wichtiger Unterschied: Während Optionen das Verhalten des Befehls verändern, geben Argumente an, worauf der Befehl angewendet werden soll.

3.1.4 Befehlsausführung und Exit-Status

Wenn ein Befehl ausgeführt wird, gibt er nach Abschluss einen Exit-Status zurück – einen numerischen Wert, der den Erfolg oder Misserfolg der Operation anzeigt:

Dieser Exit-Status kann mithilfe der speziellen Variablen $? abgefragt werden:

$ ls /existierendes/verzeichnis
[Inhalt wird angezeigt]
$ echo $?
0

$ ls /nicht/existierendes/verzeichnis
ls: cannot access '/nicht/existierendes/verzeichnis': No such file or directory
$ echo $?
2

Der Exit-Status ist besonders beim Scripting wichtig, da er verwendet wird, um den Erfolg von Operationen zu überprüfen und entsprechend zu reagieren.

3.1.5 Ein- und Ausgabeumleitung

Unix/Linux-Systeme folgen dem Prinzip, dass “alles eine Datei ist”. Diesem Paradigma folgend, behandelt die Shell Ein- und Ausgaben als Datenströme, die umgeleitet werden können. Es gibt drei Standarddatenströme:

  1. Standard Input (stdin): Kanal für Eingabedaten, standardmäßig die Tastatur
  2. Standard Output (stdout): Kanal für normale Ausgaben, standardmäßig der Bildschirm
  3. Standard Error (stderr): Kanal für Fehlermeldungen, standardmäßig ebenfalls der Bildschirm

Diese Datenströme werden durch Dateideskriptoren repräsentiert: - 0 für stdin - 1 für stdout - 2 für stderr

Die Shell bietet verschiedene Operatoren zur Umleitung dieser Datenströme:

3.1.5.1 Ausgabeumleitung

Der >-Operator leitet die Standardausgabe in eine Datei um:

$ ls -l > verzeichnis_inhalt.txt

Dieser Befehl erstellt eine neue Datei (oder überschreibt eine bestehende) und schreibt die Ausgabe von ls -l hinein. Um stattdessen an eine bestehende Datei anzuhängen, verwenden wir >>:

$ echo "Neue Zeile" >> logdatei.txt

3.1.5.2 Fehlerumleitung

Mit dem Dateideskriptor 2> können wir den Standardfehlerkanal umleiten:

$ ls /nicht/existierend 2> fehler.log

Um sowohl Standardausgabe als auch Standardfehler in dieselbe Datei umzuleiten:

$ ls -l /existierend /nicht/existierend > ausgabe.log 2>&1

Hier bedeutet 2>&1, dass der Fehlerstrom (2) dorthin umgeleitet wird, wohin der Ausgabestrom (1) zeigt.

In neueren Shell-Versionen kann dies auch kürzer geschrieben werden:

$ ls -l /existierend /nicht/existierend &> ausgabe.log

3.1.5.3 Eingabeumleitung

Der <-Operator ermöglicht die Umleitung der Standardeingabe von einer Datei:

$ wc -l < datei.txt

Dieses Beispiel zählt die Zeilen in datei.txt. Im Gegensatz zu wc -l datei.txt wird hier der Dateiname nicht in der Ausgabe angezeigt.

Eine erweiterte Form ist der Here-Document (<<), der es ermöglicht, mehrzeiligen Text direkt im Terminal als Eingabe für einen Befehl bereitzustellen:

$ cat << EOF
> Erste Zeile
> Zweite Zeile
> Dritte Zeile
> EOF
Erste Zeile
Zweite Zeile
Dritte Zeile

Eine kompaktere Variante ist der Here-String (<<<), besonders nützlich für kurze Eingaben:

$ grep "Muster" <<< "Dies ist ein Text mit einem Muster darin"
Dies ist ein Text mit einem Muster darin

3.1.6 Pipes: Verketten von Befehlen

Eine der mächtigsten Funktionen der Unix-Shell ist die Möglichkeit, Befehle mittels Pipes zu verketten. Der Pipe-Operator (|) leitet die Standardausgabe eines Befehls an die Standardeingabe eines anderen Befehls weiter:

$ ls -l | grep "Jan" | sort

Dieser Befehl listet alle Dateien im aktuellen Verzeichnis im langen Format auf, filtert dann die Zeilen, die “Jan” enthalten (z.B. Dateien, die im Januar erstellt wurden), und sortiert diese Ausgabe.

Pipes ermöglichen die Erstellung komplexer Befehlsketten, wobei jeder Befehl eine spezifische Aufgabe übernimmt. Dies folgt der Unix-Philosophie: “Schreibe Programme, die eine Sache gut machen. Schreibe Programme, die zusammenarbeiten.”

Ein praktisches Beispiel ist die Suche nach den fünf größten Dateien in einem Verzeichnisbaum:

$ find /pfad -type f -exec ls -sh {} \; | sort -rh | head -5

Dieser Befehl findet alle regulären Dateien, zeigt ihre Größe an, sortiert sie in absteigender Reihenfolge und zeigt die obersten fünf Einträge.

3.1.7 Befehlssubstitution

Die Befehlssubstitution ermöglicht es, die Ausgabe eines Befehls als Teil eines anderen Befehls zu verwenden. Es gibt zwei Syntaxvarianten:

  1. Mit Backticks (veraltet, aber noch weit verbreitet):

    $ echo "Heutiges Datum: `date`"
  2. Mit $() (moderne, besser lesbare und verschachtelbare Syntax):

    $ echo "Heutiges Datum: $(date)"

Befehlssubstitution ist besonders nützlich, um die Ausgabe von Befehlen in Variablen zu speichern:

$ aktuelles_verzeichnis=$(pwd)
$ echo "Wir befinden uns in: $aktuelles_verzeichnis"

Oder um komplexere Befehlskonstrukte zu erstellen:

$ for datei in $(find . -name "*.txt"); do
>   echo "Gefunden: $datei"
> done

3.1.8 Kontrolloperatoren für bedingte Ausführung

Die Shell bietet Operatoren, um die Ausführung von Befehlen abhängig vom Erfolg oder Misserfolg vorheriger Befehle zu steuern:

  1. AND-Operator (&&): Der zweite Befehl wird nur ausgeführt, wenn der erste erfolgreich war (Exit-Status 0):

    $ mkdir neues_verzeichnis && cd neues_verzeichnis

    Hier wechseln wir nur in das Verzeichnis, wenn es erfolgreich erstellt wurde.

  2. OR-Operator (||): Der zweite Befehl wird nur ausgeführt, wenn der erste nicht erfolgreich war (Exit-Status ungleich 0):

    $ grep "pattern" datei.txt || echo "Muster nicht gefunden"

    Diese Zeile sucht nach einem Muster und gibt eine Nachricht aus, wenn es nicht gefunden wurde.

Diese Operatoren können kombiniert werden, um komplexere bedingte Logik zu implementieren:

$ ./kompilieren && ./tests || echo "Entwicklungsprozess fehlgeschlagen"

Dieser Befehl führt die Tests nur aus, wenn das Kompilieren erfolgreich war, und gibt eine Fehlermeldung aus, wenn entweder das Kompilieren oder die Tests fehlschlagen.

3.1.9 Hintergrundprozesse

Normalerweise blockiert ein Befehl das Terminal, bis er abgeschlossen ist. Um einen Befehl im Hintergrund auszuführen und das Terminal sofort wieder nutzen zu können, kann der &-Operator verwendet werden:

$ langwieriger_prozess &
[1] 12345

Die Shell gibt eine Job-Nummer (in eckigen Klammern) und eine Prozess-ID zurück.

Mit dem Befehl jobs können laufende Hintergrundprozesse angezeigt werden:

$ jobs
[1]+  Running                 langwieriger_prozess &

Mit fg kann ein Hintergrundprozess in den Vordergrund geholt werden:

$ fg %1

Um einen laufenden Vordergrundprozess zu unterbrechen und in den Hintergrund zu verschieben: 1. Drücke Ctrl+Z, um den Prozess zu pausieren 2. Führe bg aus, um ihn im Hintergrund fortzusetzen

$ langwieriger_prozess
^Z
[1]+  Stopped                 langwieriger_prozess
$ bg
[1]+ langwieriger_prozess &

3.1.10 Platzhalter und Musterabgleich (Globbing)

Die Shell bietet Platzhalterzeichen (“Wildcards”), um mit mehreren Dateien gleichzeitig zu arbeiten:

  1. Sternchen (*): Steht für beliebig viele Zeichen (auch keine)

    $ ls *.txt        # Listet alle .txt-Dateien
    $ rm dokument*    # Löscht alle Dateien, die mit "dokument" beginnen
  2. Fragezeichen (?): Steht für genau ein beliebiges Zeichen

    $ ls datei?.txt   # Findet datei1.txt, dateia.txt, aber nicht datei10.txt
  3. Zeichenklassen ([...]): Steht für ein einzelnes Zeichen aus einer definierten Menge

    $ ls datei[1-3].txt   # Findet datei1.txt, datei2.txt, datei3.txt
    $ ls [aA]usgabe.log   # Findet ausgabe.log und Ausgabe.log
  4. Negierte Zeichenklassen ([!...] oder [^...]): Steht für ein einzelnes Zeichen, das nicht in der definierten Menge enthalten ist

    $ ls datei[!0-9].txt  # Findet dateien ohne Ziffern am Ende

Diese Platzhalter werden von der Shell selbst verarbeitet (nicht vom aufgerufenen Programm), bevor der Befehl ausgeführt wird – ein Prozess, der als “Globbing” bezeichnet wird.

3.1.11 Befehlshistorie

Moderne Shells speichern eine Historie der eingegebenen Befehle, was die Wiederverwendung und Anpassung früherer Befehle erleichtert:

Die Taste “Pfeil nach oben” durchsucht die Historie rückwärts, während Ctrl+R eine inkrementelle Suche in der Historie ermöglicht:

$ [Drücke Ctrl+R]
(reverse-i-search)`find': find /home -name "*.conf"

3.1.12 Abschluss und nächste Schritte

Die hier vorgestellten Konzepte bilden das Fundament für die effektive Nutzung der Kommandozeile. Mit diesen Grundlagen können Benutzer bereits eine Vielzahl von Aufgaben erledigen und beginnen, die Macht der Shell zu nutzen. In den folgenden Kapiteln werden wir darauf aufbauen und fortgeschrittenere Konzepte wie Shell-Variablen, Kontrollstrukturen und komplexe Skripterstellung kennenlernen.

Die Beherrschung dieser grundlegenden Konzepte erfordert Übung. Experimentieren Sie mit den vorgestellten Befehlen und kombinieren Sie sie auf unterschiedliche Weise. Je mehr Zeit Sie mit der Kommandozeile verbringen, desto intuitiver wird die Interaktion.