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.
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/benutzerIn 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.
Befehle in Unix/Linux-Systemen können aus verschiedenen Quellen stammen:
cd, echo oder
pwd.PATH aufgeführt sind.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/gccUm 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:/sbinOptionen (auch Flags oder Schalter genannt) modifizieren das Verhalten eines Befehls. Es gibt zwei gängige Formate für Optionen:
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.
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 TrefferArgumente 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.
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:
0 bedeutet erfolgreiche Ausführung0 (typischerweise 1-255) zeigt
einen Fehler anDieser 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 $?
2Der Exit-Status ist besonders beim Scripting wichtig, da er verwendet wird, um den Erfolg von Operationen zu überprüfen und entsprechend zu reagieren.
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:
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:
Der >-Operator leitet die Standardausgabe in eine
Datei um:
$ ls -l > verzeichnis_inhalt.txtDieser 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.txtMit dem Dateideskriptor 2> können wir den
Standardfehlerkanal umleiten:
$ ls /nicht/existierend 2> fehler.logUm sowohl Standardausgabe als auch Standardfehler in dieselbe Datei umzuleiten:
$ ls -l /existierend /nicht/existierend > ausgabe.log 2>&1Hier 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.logDer <-Operator ermöglicht die Umleitung der
Standardeingabe von einer Datei:
$ wc -l < datei.txtDieses 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 ZeileEine 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 darinEine 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" | sortDieser 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 -5Dieser Befehl findet alle regulären Dateien, zeigt ihre Größe an, sortiert sie in absteigender Reihenfolge und zeigt die obersten fünf Einträge.
Die Befehlssubstitution ermöglicht es, die Ausgabe eines Befehls als Teil eines anderen Befehls zu verwenden. Es gibt zwei Syntaxvarianten:
Mit Backticks (veraltet, aber noch weit verbreitet):
$ echo "Heutiges Datum: `date`"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"
> doneDie Shell bietet Operatoren, um die Ausführung von Befehlen abhängig vom Erfolg oder Misserfolg vorheriger Befehle zu steuern:
AND-Operator (&&): Der
zweite Befehl wird nur ausgeführt, wenn der erste erfolgreich war
(Exit-Status 0):
$ mkdir neues_verzeichnis && cd neues_verzeichnisHier wechseln wir nur in das Verzeichnis, wenn es erfolgreich erstellt wurde.
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.
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] 12345Die 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 %1Um 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 &Die Shell bietet Platzhalterzeichen (“Wildcards”), um mit mehreren Dateien gleichzeitig zu arbeiten:
Sternchen (*): Steht für beliebig
viele Zeichen (auch keine)
$ ls *.txt # Listet alle .txt-Dateien
$ rm dokument* # Löscht alle Dateien, die mit "dokument" beginnenFragezeichen (?): Steht für genau
ein beliebiges Zeichen
$ ls datei?.txt # Findet datei1.txt, dateia.txt, aber nicht datei10.txtZeichenklassen ([...]): 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.logNegierte 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 EndeDiese Platzhalter werden von der Shell selbst verarbeitet (nicht vom aufgerufenen Programm), bevor der Befehl ausgeführt wird – ein Prozess, der als “Globbing” bezeichnet wird.
Moderne Shells speichern eine Historie der eingegebenen Befehle, was die Wiederverwendung und Anpassung früherer Befehle erleichtert:
history: Zeigt die Liste der zuletzt verwendeten
Befehle an!!: Wiederholt den letzten Befehl!n: Führt den Befehl mit der Nummer n aus der Historie
aus!-n: Führt den n-letzten Befehl aus!string: Führt den letzten Befehl aus, der mit “string”
begann^alt^neu: Ersetzt “alt” durch “neu” im letzten Befehl
und führt ihn ausDie 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"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.