Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Einleitung

Willkommen zur macOS Terminal Referenz

Das Terminal ist weit mehr als ein schwarzes Fenster mit grünem Text – es ist das mächtigste Werkzeug auf deinem Mac. Während die grafische Oberfläche ihre Berechtigung hat, ermöglicht das Terminal eine Kontrolle und Effizienz, die mit Mausklicks unerreichbar bleibt.

Warum das Terminal beherrschen?

Das macOS-Terminal bzw. die Shell (standardmäßig zsh seit macOS Catalina) ist die direkte und mächtigste Schnittstelle zum Betriebssystem. Es ermöglicht:

  • Geschwindigkeit: Navigation mit der Tastatur – keine Mausklicks, keine Verzögerungen
  • Automatisierung: Komplexe Aufgaben mit einem einzigen Befehl wiederholen
  • Kontrolle: Zugriff auf (versteckte) Systemeinstellungen und Systemdateien
  • Entwicklung: Konfiguration von Entwicklungsumgebungen, Verwaltung von Git-Repositories, Deployment von Code
  • Fehleranalyse: Durchsuchen von Logs, Analysieren von Prozesse, Verstehen von Systeminformationen
  • Server-Verwaltung: Effiziente Verwaltung von Cloud-Systemen und Remote-Servern

note

Mit dem Terminal hat man die volle Kontrolle über das System. Es ist kein Relikt aus vergangenen Zeiten, sondern das präziseste und schnellste Werkzeug des Betriebssystems.

Inhalte

Diese Referenz ist in vier Teile gegliedert:

Teil I: Grundlagen

Essentiellen Befehle, Verständnis für das Dateisystem, Konfiguration der Shell, eine Basis für effizientes Arbeiten im Terminal legen

Teil II: Fortgeschrittene Themen

Von Shell-Scripting über tmux bis zu ZLE-Widgets – hier wird tiefer eingetaucht und erklärt, wie du das Terminal zum persönlichen Produktivitäts-Multiplikator wird.

Teil III: Cheat Sheets

Schnellreferenzen für häufig verwendete Tools und Programmiersprachen – immer griffbereit.

Teil IV: Terminals, Shells und Erweiterungen

Moderne Terminal-Emulatoren, Shell-Erweiterungen und die neuesten CLI-Tools, welche die Arbeit noch angenehmer machen.

Wie diese Referenz genutzt werden kann

Diese Referenz ist als Nachschlagewerk konzipiert. Man muss sie nicht von vorne bis hinten durcharbeiten:

  • Als Anfänger: Start mit Teil I und sequentielles Durcharbeiten der Grundlagen
  • Als Fortgeschrittener: Direkt zu den Themen, die einen interessieren
  • Im Alltag: Nutzung der Suchfunktion und Cheat Sheets als schnelle Referenz

Jedes Kapitel enthält praktische Beispiele, die man direkt in deinem Terminal ausprobieren kann.

Terminal und Dateisystem

1 Was ist das Terminal

Das Terminal ist ein Programm, dass eine Shell, d. h. einen Kommandozeileninterpreter, ausführt. macOS nutzt standardmäßig zsh (Z Shell). Es wird im folgenden davon ausgegangen, dass zsh verwendet wird.

Welche Shell genutzt wird, kann man dem folgenden Befehl geprüft werden:

echo $SHELL

2 Wichtige Konzepte/Begriffe

BegriffBedeutung
ShellProgramm, das Befehle interpretiert (zsh, bash, fish etc.)
PromptDie Eingabeaufforderung, z. B. username@Mac ~ %
Pfad (Path)Gibt an, wo im Dateisystem man sich befindest
Home-VerzeichnisPfad des persönlichen Bereichs: ~ oder /Users/<username>
RootDas oberste Verzeichnis des Systems: /
UmgebungsvariablenWerte, die Systemverhalten beeinflussen, z. B. PATH, HOME

3 Dateisystem von macOS

3.1 Aktuelles Dateisystem: APFS (Apple File System)

Seit macOS 10.13 (High Sierra, 2017) ist APFS das Standard-Dateisystem für SSDs; seit macOS 10.15 (Catalina, 2019) auch für alle Laufwerke.

Merkmale:

  • Copy-on-Write (CoW): Änderungen an Dateien oder Metadaten erzeugen neue Kopien, was das System sicherer macht.

    (Änderungen werden nicht direkt auf bestehende Daten geschrieben werden. Stattdessen legt das System beim Ändern von Dateien oder Metadaten eine neue Kopie der betroffenen Blöcke an und verweist erst danach auf sie. Dadurch bleiben alte Datenversionen solange erhalten, bis der Schreibvorgang erfolgreich abgeschlossen ist. Das schützt vor Datenverlust bei Stromausfall oder Absturz. Außerdem ermöglicht CoW Systemfunktionen wie Snapshots, bei denen ganze Dateisystemzustände nahezu ohne Speicheraufwand gespeichert werden.)

  • Snapshots: Das System kann zu bestimmten Zeitpunkten eingefroren werden (z. B. vor Systemupdates). Dadurch kann bei Problemen auf einen alten Zustand zurückgewechselt werden. Time Machine nutzt diese Funktion.

  • Space Sharing: Mehrere Volumes (z. B. “Macintosh HD” und “Macintosh HD – Data”) teilen sich denselben physischen Speicherplatz. Das spart Platz, da ungenutzter Speicher flexibel zwischen Volumes verteilt wird.

  • Verschlüsselung: Native Unterstützung für FileVault 2 und einzelne verschlüsselte Volumes. (FileVault 2 verschlüsselt das gesamte Systemvolume, schützt Daten bei Diebstahl und sorgt dafür, dass nur berechtigte Benutzer darauf zugreifen können.)

  • Case Sensitivity: Standardmäßig nicht case-sensitive (d. h. Datei.txt = datei.txt), kann aber beim Formatieren geändert werden.

3.2 Wichtige Volumes und Partitionen

Seit macOS Catalina ist das System in mehrere logische Volumes aufgeteilt:

  • Macintosh HD: Systemvolume (read-only, geschützt durch SIP) (Das Systemvolume enthält das schreibgeschützte Betriebssystem inklusive aller Kernkomponenten, Frameworks und Standardprogramme.)

  • Macintosh HD – Data: Benutzer- und Programmdaten (beschreibbar) persönliche Dateien, Dokumente, Einstellungen und installierte Programme, z. B. in /Users/<username> oder /Applications.

  • Preboot, Recovery, VM: Hilfsvolumes für Boot, Wiederherstellung und Swap Preboot hilft beim Starten des Systems, Recovery ermöglicht Wiederherstellung und VM speichert Swap- bzw. Auslagerungsdateien temporär.

Mit dem Befehl diskutil wird die komplette Volume-Struktur angezeigt.

3.3 Systemschutz und Berechtigungen

macOS schützt kritische Systembereiche durch verschiedene Mechanismen, damit Systemdateien nicht versehentlich verändert werden können.

  • SIP (System Integrity Protection): Schützt Systemverzeichnisse und Kernel-Extensions. Nur im Recovery-Modus deaktivierbar.

  • Sandboxing: Viele Apps laufen in isolierten Containern, sodass sie nur auf erlaubte Dateien zugreifen können. Dies begrenzt Schäden durch fehlerhafte oder schadhafte Programme.

  • ACLs & POSIX Permissions: macOS verwendet klassische Unix-Rechte (rwx, siehe ../SUMMARY) und erweiterte Access Control Lists (siehe ../SUMMARY). → Anzeigen mit ls -le.

3.4 Mountpoints und Pfade

  • Alle Volumes werden unter /Volumes/ eingehängt.
  • Netzlaufwerke oder externe Festplatten erscheinen dort automatisch.
  • Systempfade wie /System, /Library oder /usr sind meist Symlinks auf interne Strukturen des schreibgeschützten Systemvolumes.

4 Ordnerstruktur und wichtige Verzeichnisse

macOS basiert auf UNIX (BSD), daher folgt es grob der klassischen Unix-Hierarchie – erweitert um Apple-spezifische Pfade.

4.1 Systemebene (Root /)

OrdnerBeschreibung
/SystemEnthält das Betriebssystem selbst (Frameworks, Libraries, Kernel-Komponenten). Schreibgeschützt
/binEnthält grundlegende Benutzerbefehle, die auch im Einzelbenutzermodus verfügbar sein müssen, wie beispielweise ls, cp, mv, rm, bash oder zsh. Diese Tools sind für den täglichen Gebrauch und die Systemreparatur unverzichtbar.
/sbinBeinhaltet System- und Verwaltungsbefehle, die normalerweise Administratorrechte erfordern, z. B. ifconfig, mount, fsck oder reboot. Diese Tools dienen zur Systemwartung und -konfiguration.
/usr/binHier liegen zusätzliche Benutzerprogramme und Utilities, die nicht unbedingt zum Start des Systems benötigt werden, z. B. grep, awk, vim, curl, python, git usw.
/usr/sbinErgänzt /usr/bin um administrative Befehle wie apachectl, postfix, cron oder syslogd. Sie werden meistens von Diensten oder Admins verwendet, nicht von normalen Nutzern.
/usr/localFür manuell installierte Tools und Homebrew
/LibrarySystemweite Ressourcen: LaunchAgents, Fonts, Extensions, Preferences
/ApplicationsSystemweite Programme (z. B. Safari, Mail)
/VolumesMountpoint für externe Laufwerke, Netzfreigaben, Disk-Images
/privateEnthält temporäre Daten (/private/tmp, /private/var) und Logdateien. Viele klassische UNIX-Verzeichnisse (/etc, /tmp, /var) sind Symlinks hierhin

note

Viele dieser Pfade sind symbolisch mit /System/Volumes/Data/usr/... verknüpft, da das eigentliche Systemvolume seit macOS Catalina schreibgeschützt ist.

4.2 Benutzerverzeichnisse (/Users)

Jeder Benutzer hat ein eigenes Homeverzeichnis, z. B. /Users/john.

Wichtige Unterordner:

OrdnerBeschreibung
DesktopDateien auf dem Schreibtisch.
DocumentsEigene Dokumente.
DownloadsStandard-Download-Ordner.
LibraryBenutzerbezogene Einstellungen, Caches, LaunchAgents, App-Daten.
Pictures, Music, MoviesMedienordner (z. B. für Fotos oder iTunes).

note

Der Ordner ~/Library ist standardmäßig versteckt. Anzeigen mit:

chflags nohidden ~/Library

important

Tilde (~)

Das Zeichen ~ steht im Terminal als Abkürzung für das aktuelle Benutzerverzeichnis, z. B. /Users/john. Es wird getippt mit ⌥ N.

Mit cd ~ kann man in das Homeverzeichnis zu wechseln oder mit cd ~/Documents, direkt in den Dokumente-Ordner.

4.3 Weitere wichtige Orte

PfadZweck
/etc/hostsLokale Hostname-Zuordnungen.
/var/log/System- und Anwendungs-Logs.
/private/tmpTemporäre Dateien (automatisch geleert).
/Library/LaunchAgents
und
/Library/LaunchDaemons
Automatische Dienste und Hintergrundprozesse.
/usr/share/Gemeinsame Systemdateien (z. B. Manpages).

5 macOS unter der Haube

macOS basiert auf UNIX/BSD und Darwin. Unter der Haube steuern Launchd, Logging und Sicherheitsmechanismen, wie Gatekeeper oder TCC, den Systembetrieb.

5.1 Launchd und Dienste

launchd ist das zentrale Init-System von macOS und ersetzt klassische Systeme wie systemd oder init. Es startet Prozesse beim Booten, nach Bedarf oder im Benutzerkontext. Dienste werden über Property-List-Dateien (.plist) definiert.

Speicherort der .plist-Dateien:

  • /System/Library/LaunchDaemons,
  • /Library/LaunchAgents oder
  • ~/Library/LaunchAgents

Aktive Dienste anzeigen:

launchctl list

Manuelle Steuerung von Diensten:

launchctl load/unload

Damit verwaltet macOS sowohl System- als auch Benutzerprozesse konsistent.

5.2 Systemprotokolle und Logging

macOS verwendet seit Sierra ein Unified Logging System, das alle Logmeldungen zentral verfügbar macht.

Vergangene Ereignisse anzeigen:

log show

Live Ansicht von Ereignissen:

log stream

Diese Logs sind strukturiert und nach Subsystemen gegliedert, z. B. “network”, “kernel” oder “security”.

Ältere Textlogs existieren weiterhin unter /var/log/, z. B. system.log. Entwickler oder Administratoren können mit Filtern wie

log show --predicate 'eventMessage contains "error"'

gezielt Fehlermeldungen finden.

5.3 Netzwerk & Namensauflösung

macOS bietet zahlreiche Kommandozeilenwerkzeuge zur Verwaltung und Diagnose von Netzwerken. Neben klassischen UNIX-Befehlen wie ping und traceroute sind vor allem networksetup und scutil für Systemkonfigurationen relevant.

Über den Dienst mDNSResponder (Bonjour) werden Geräte im lokalen Netz automatisch erkannt. Die Datei /etc/hosts erlaubt lokale DNS-Zuordnungen.

BefehlBeschreibungBeispiel
networksetupZeigt oder ändert Netzwerk-Interfaces, Dienste und DNS-Einstellungennetworksetup -listallhardwareports
ifconfigZeigt IP-Konfigurationen und Netzwerkinterfaces an oder konfiguriert sie manuellifconfig en0
pingPrüft, ob ein Host erreichbar istping google.com
tracerouteZeigt die Route von Paketen zum Zielhost antraceroute apple.com
scutil --dnsListet DNS-Server, Suchdomänen und aktuelle Resolver-Einstellungen aufscutil --dns
cat /etc/hostsZeigt lokale Hostnamen-Zuordnungen an oder ermöglicht deren Bearbeitungsudo nano /etc/hosts

5.4 Sicherheitsmechanismen

Die folgenden Mechanismen/Schutzebenen wirken zusammen, um das System vor Malware und unautorisierten Änderungen zu schützen:

  • Gatekeeper blockiert unsignierte oder nicht-notarisierte Apps.
  • SIP (System Integrity Protection) schützt Systemverzeichnisse und Kernel-Komponenten selbst vor Root-Änderungen.
  • Notarization stellt sicher, dass Apps von Apple geprüft wurden.
  • Sandboxing und TCC (Transparency, Consent & Control) begrenzen App-Zugriffe auf Dateien, Kamera, Mikrofon usw.

5.5 Prozess- & Systemüberwachung

Es gibt mehrere Tools, um Prozesse, Ressourcen und Systemzustände zu überwachen. Während ps und top Standardbefehle aus der UNIX-Welt sind, liefert htop eine moderne, farbige Ansicht. Befehle wie lsof, vm_stat und powermetrics helfen bei tiefergehender Analyse von Performance, Speicher oder Energieverbrauch.

BefehlBeschreibungBeispiel
ps auxListet alle laufenden Prozesse mit Benutzer, PID, CPU- und Speicherverbrauchps aux
topZeigt Systemauslastung in Echtzeit an (ähnlich Task-Manager)top
htopErweiterte, interaktive Prozessansicht mit Sortierung, Suche und Farbenhtop
lsofListet offene Dateien, Ports und Socketssudo lsof -i :80
vm_statZeigt Statistik zur Speichernutzung und Paging-Aktivitätvm_stat
powermetricsMisst Energieverbrauch, CPU-Last, GPU-Aktivität und Temperatursudo powermetrics --samplers cpu_power

Die wichtigsten Befehle

1 Navigation

BefehlBeschreibung
pwdZeigt aktuelles Verzeichnis
lsListet Dateien/Ordner
cdWechselt Verzeichnis
historyZeigt den Befehlsverlauf an

1.1 ls-Befehl

ls -l    # Detaillierte Liste mit Berechtigungen, Datum, Größe, ...
ls -a    # Zeigt auch versteckte Dateien und Ordner an
ls -la   # Kombination: Detaillierte Liste inkl. versteckter Elemente
ls -lh   # Liste mit Größenangaben in passender Einheit (KB, MB, GB)
ls -lah  # Kombination
ls -lt   # Sortierung nach Änderungsdatum
ls -lS   # Sortierung nach Dateigröße
ls -l <pfad>  # Anzeigen von Dateien/Ordner des gegebenen Pfads

1.2 cd-Befehl

cd Documents    # Wechselt in den Unterordner Documents
cd ~/Downloads  # Wechselt in den Ordner Downloads im Homeverzeichnis
cd /      # Wechselt in Hauptverzeichnis
cd ~      # Wechselt in das Homeverzeichnis
cd ..     # Wechselt ins Eltern-Verzeichnis
cd ../..  # Geht zwei Ebenen nach oben
cd -      # Springt zum letzten Verzeichnis zurück

note

Unterschied zwischen ~ und $HOME

Die Tilde (~) ist eine Shell-Abkürzung für das Home-Verzeichnis, während $HOME eine Umgebungsvariable ist, die von Programmen und Skripten verwendet wird. Beide zeigen auf das Home-Verzeichnis des aktuell angemeldeten Benutzers, sind jedoch unterschiedlich implementiert.

Siehe auch: ~ vs. $HOME

1.3 history-Befehl

history               # Zeigt die letzten ausgeführten Befehle an
history | grep "cal"  # Zeigt die letzten Befehle an, passen zum Suchbegriff
!123                  # Führt Befehl 123 erneut aus
!!                    # Führt den letzten Befehl erneut aus

2 Dateien und Ordner

BefehlBeschreibungBeispiel
touchErstellt leere Datei(ein)touch test1.txt test2.txt
mkdirErstellt Verzeichnis(se)mkdir Projekte Archiv
cpKopiert Datei/Ordnercp quelle.txt ziel.txt
mvVerschiebt oder benennt ummv alt.txt neu.txt
rmLöscht Datei(en) und Ordnerrm test1.txt test2.txt
rm -rLöscht Verzeichnis rekursivrm -r Ordner

achtung

Der Befehl rm löscht unwiderruflich – ohne Papierkorb!

Soll das Löschen bestätigt werden, muss der interaktive Modus verwendet werden:

>rm -i test.txt
>```

</div>


<div class="mdbook-callouts mdbook-callouts-tip">
<p class="mdbook-callouts-title">
  <span class="mdbook-callouts-icon"></span>
  tip
</p>


Bevor mehrere Dateien gelöscht werden, sollen die Auswirkungen des zugehörigen Befehls geprüft werden. Beispiel:

```zsh
ls *.py
rm *.py

2.1 mkdir-Befehl

mkdir NeuerOrdner          # Erstellt einen Ordner
mkdir Ordner1 Ordner2      # Erstellt mehrere Ordner
mkdir -p Fotos/2025/10/29  # Erstellt Ordner + Unterordner

Brace Expansion für effiziente Stapelerstellung:

Geschweifte Klammern ermöglichen die schnelle Erstellung mehrerer ähnlicher Elemente:

mkdir Tag_{01..03}           # Erstellt: Tag_01, Tag_02, Tag_03
mkdir {Januar,Februar,März}  # Erstellt drei Monatsordner
touch datei_{a,b,c}.txt      # Erstellt: datei_a.txt, datei_b.txt, datei_c.txt
echo {01..05}                # Zeigt: 01 02 03 04 05
cp dokument.txt{,.backup}    # Kopiert nach dokument.txt.backup

Kombinationen sind ebenfalls möglich:

mkdir -p Projekt/{src,tests,docs}/{python,rust}
# Erstellt: Projekt/src/python, Projekt/src/rust,
#           Projekt/tests/python, Projekt/tests/rust,
#           Projekt/docs/python, Projekt/docs/rust

2.2 cp- und mv-Befehl

cp quelle.txt ziel.txt     # Kopiert eine Datei
cp -i quelle.txt ziel.txt  # Fragt nach bevor überschrieben wird
cp -r Ordner1 Ordner2      # Kopiert einen kompletten Ordner (rekursiv)
cp *.txt Text              # Kopiert alle Dateien mit der Endung .txt

Gleiche Syntax beim mv-Befehl.

3 Arbeiten mit Texten und Dateien

BefehlFunktionBeispiel
catZeigt den Inhalt von Dateien (ohne Editor)cat datei1.txt datei2.txt
lessScrollbare Ansichtless datei.txt
head / tailZeigt Anfang/Endehead -n 20 datei.txt
wcZählt Zeilen, Wörter, Zeichenwc -l datei.txt
diffVergleicht Dateiendiff a.txt b.txt
findSucht Dateienfind . -name "*.txt"
nanoDatei im Texteditor im Terminal öffnennano datei.txt
grepSucht nach Textmustern in Dateiengrep "Fehler" log.txt
openÖffnet Datei/Ordner im Finderopen .
open -a <App>Öffnet Datei/Ordner in bestimmter Appopen -a ForkLift .

Alternative zu less datei.txt: cat datei.txt | more

3.1 Navigation bei Anzeige über less-Befehl

  • Leertaste: Nächste Seite
  • b: Vorherige Seite
  • /: Vorwärts suchen
  • ?: Rückwärts suchen
  • q: Beenden

3.2 head- und tail-Befehl

head datei.txt        # Zeigt die ersten 20 Zeilen
head -n 20 datei.txt  # Zeigt die ersten 20 Zeilen
tail datei.txt        # Zeigt die letzten 10 Zeilen
tail -n 20 datei.txt  # Zeigt die letzten 20 Zeilen

3.3 Dateiinhalt in Echtzeit anzeigen

Änderungen in Echtzeit können mit folgendem Befehl verfolgt werden:

tail -f info.log

Dies funktioniert jedoch nicht immer. Viele Editoren speichern Änderungen nicht direkt in der bestehenden Datei, sondern erstellen eine temporäre Datei, schreiben den neuen Inhalt hinein, löschen die alte Datei und benennen die neue Datei auf den alten Namen um. Dabei ändert sich die inode (Datei-Identität im Dateisystem). Abhilfe schafft -F (follow name). Es wird erkannt, wenn die Datei ersetzt wurde und folgt automatisch der neuen Datei.

tail -F info.log

Hierbei wird jedoch immer der neue Dateiinhalt unter den alten geschrieben. Dies wird schnell unübersichtlich. Eine bessere Lösung ist mit dem Befehl watch möglich. Diese muss ggf. noch installiert werden:

brew install watch

Anschließend kann der Dateiinhalt wie folgt überwacht werden:

watch -n 1 "clear && cat info.log"
  • -n 1: alle 1 Sekunden aktualisieren
  • clear: Ausgabe löschen
  • cat test.txt: Dateiinhalt anzeigen

Nutzt man dies öfters, empfiehlt sich die Erstellung eines Alias:

alias watchfile='watch -n 1 "clear && cat $1"'

Verwendung mit:

watchfile info.log

3.4 find-Befehl

find . -name "*.txt"          # Findet alle .txt-Dateien
find . -name "rechnung.pdf"   # Findet die Datei rechnung.pdf
find . -type f -size +5M      # Findet Dateien größer als 5 MB
find . -name "*.*" -mtime -7  # Dateien, die in den letzten 7 Tagen verändert wurden
find . -empty                 # Findet leere Dateien und Ordner

3.5 grep-Befehl

grep "Januar" notiz.txt     # Sucht in einer Datei
grep -r "Januar" notiz.txt  # Sucht rekursiv in einem Ordner
grep -n "Januar" notiz.txt  # Zeigt alle Zeilen inkl. Nr. mit dem Suchwort
grep -c "Januar" notiz.txt  # Zählt die Anzahl der Vorkommen des Suchworts
grep -i "Januar" notiz.txt  # Berücksichtigt Groß- und Kleinschreibung

3.6 Prozesse, Systeme und Netzwerk

3.7 Prozesse

BefehlBeschreibungBeispiel
topLive-Systemüberwachungtop
htopLive-Systemüberwachung (interaktiv, in Farbe)htop
psZeigt aktive Prozesseps aux
killBeendet Prozesskill 1234
killallBeendet alle Prozesse eines Namenskillall Safari

3.8 top-Befehl

Kurzbefehle:

  • o: Nach anderer Spalte sortieren (o drücken und Spaltenname eingeben)
  • q: Beenden

Alternative: htop

htop liefert eine interaktive Anzeige in Farbe.

Installation:

brew install htop

3.9 ps-Befehl

ps                    # Zeigt die eigenen Prozesse
ps aux                # Zeigt alle Prozesse mit Details
ps aux | grep Safari  # Zeigt bestimmte Prozesse

3.10 System und Hardware

BefehlBeschreibung
df -hSpeicherplatz aller Laufwerke anzeigen
du -sh *Größe von Ordnern anzeigen
unameAnzeige von Systeminformationen
uptimeZeigt Laufzeit und Systemlast
whoamiAktueller Benutzer
dateAktuelles Datum/Zeit
calZeigt einen Kalender an
say "Hallo"macOS sagt Text laut (praktisch für Testzwecke)

3.11 du-Befehl

du -sh * | sort -h   # Größe von Ordnern und Dateien aufsteigend sortiert
du -sh * | sort -hr  # Absteigend sortiert
du -sh * | sort -hr | head -5  # Beschränkung auf die 5 größten Elemente

3.12 uname-Befehl

uname     # Zeigt den Kernelnamen an (i. d. R. "Darwin")
uname -a  # Alle verfügbaren Systeminformationen
uname -s  # Gleiche ausgabe wie bei uname
uname -r  # Kernel-Version
uname -v  # Kernel-Buildversion
uname -m  # Machine hardware name
uname -n  # Rechnername (Hostname)
uname -p  # Prozessortyp (machmal leer)
uname -o  # Betriebssystem

3.13 date-Befehl

date              # Zeigt Datum+Zeit; z. B.: Thu Oct 30 21:53:42 CET 2025
date "+%Y-%m-%d"  # Zeigt Datum, z. B.: 2025-10-30
date "+%H:%M:%S"  # Zeigt Uhrzeit, z. B.: 21:53:42

3.14 cal-Befehl

cal          # Aktueller Monat
cal 2026     # Bestimmtes Jahr
cal 01 2026  # Bestimmter Monat

Beispielausgabe:

    January 2026
Su Mo Tu We Th Fr Sa
             1  2  3
 4  5  6  7  8  9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31

3.15 Netzwerk

BefehlBeschreibungBeispiel
pingTestet Verbindungping google.com
ifconfigZeigt Netzwerkschnittstellenifconfig
curlHTTP-Anfragen sendencurl https://example.com
scpKopiert Dateien über SSHscp file.txt user@host:/path
sshVerbindung zu Serverssh user@ip

Lokale IP-Adresse(n) anzeigen:

ifconfig | grep "inet "

3.16 curl-Befehl

curl ist ein vielseitiges Tool zum Abrufen oder Senden von Daten über HTTP, HTTPS, FTP und viele andere Protokolle. Es eignet sich sowohl zum einfachen Testen von Webseiten als auch für API-Aufrufe.

Beispiele:

# Webseite abrufen und im Terminal anzeigen
curl https://example.com

# Datei herunterladen
curl -O https://example.com/datei.zip

# API-Aufruf mit Header
curl -H "Authorization: Bearer TOKEN" https://api.example.com/data

Häufig verwendete Optionen:

  • -I: nur Header anzeigen
  • -L: Redirects folgen
  • -d: POST-Daten senden

4 Job-Control (fg, bg, jobs, &)

Job-Control ermöglicht die Verwaltung von Prozessen, die im Hintergrund oder Vordergrund laufen. Dies ist besonders nützlich bei langläufigen Aufgaben oder wenn man mehrere Programme parallel ausführen möchte.

4.1 Grundlegende Konzepte

  • Vordergrundprozess (Foreground): Prozess, der gerade aktiv ist und das Terminal blockiert

  • Hintergrundprozess (Background): Prozess, der im Hintergrund läuft, während das Terminal weiter nutzbar bleibt

  • Job: Eine vom Terminal gestartete Aufgabe (kann mehrere Prozesse enthalten)

4.2 Befehle

BefehlBeschreibung
jobsZeigt alle Jobs der aktuellen Shell
fgHolt einen Job in den Vordergrund
bgSetzt einen angehaltenen Job im Hintergrund fort
&Startet einen Befehl direkt im Hintergrund
STRG + ZHält den aktuellen Vordergrundprozess an

4.3 Beispiele

Prozess im Hintergrund starten:

python script.py &

Das & am Ende startet den Befehl sofort im Hintergrund. Das Terminal bleibt nutzbar.

Laufenden Prozess anhalten und in Hintergrund verschieben:

python script.py
# STRG + Z drücken (Prozess wird angehalten)
bg

Alle Jobs anzeigen:

jobs

Beispielausgabe:

[1]  + running    python script.py
[2]  - suspended  vim dokument.txt

Bestimmten Job in den Vordergrund holen:

fg %1    # Job 1 in den Vordergrund
fg %2    # Job 2 in den Vordergrund
fg       # Letzten Job in den Vordergrund

Hintergrundprozess beenden:

kill %1  # Beendet Job 1

4.4 Praktische Anwendungsfälle

Mehrere Editoren gleichzeitig:

vim config.txt &
vim script.py &
jobs

Langläufige Aufgabe im Hintergrund:

find / -name "*.log" > logs.txt 2>&1 &

Zwischen mehreren Aufgaben wechseln:

python server.py
# STRG + Z
bg
vim main.py
# STRG + Z
jobs
fg %1  # Zurück zum Server

Arbeitsumgebung und Konfiguration

1 Der which-Befehl

Der Befehl which zeigt den vollständigen Pfad des Kommandos, das übergeben wird:

which <befehlsname>

Gibt man einen Befehl im Terminal ein, sucht die Shell in allen Verzeichnissen der PATH-Variable nach einer Datei mit diesem Namen. which tut dies ebenfalls. Jedoch wird der Befehl nicht ausgeführt sondern sein Speicherort ausgegeben.

Beispiel:

which brew

liefert:

/opt/homebrew/bin/brew

Wird nach einem Shell-Builtin (einem eingebauten Befehl, welcher kein separates Programm ist) wie beispielsweise cd oder echo gesucht, wird folgendes ausgegeben:

cd: shell built-in command

Vergleich mit ähnlichen Befehlen:

BefehlZweck
which <cmd>Zeigt, wo auf dem Dateisystem ein ausführbares Programm gefunden wird.
command -v <cmd>Zeigt, wie die Shell den Befehl auflöst (auch für Builtins/Aliase).
type <cmd>Noch detailliertere Information über den Befehlstyp.

2 Umgebungsvariablen

Anzeige einer Umgebungsvariable:

echo $PATH

Erweiterung von $PATH:

export PATH=$PATH:/usr/local/bin

2.1 Dauerhafte Speicherung

Nach einem Neustart sind zusätzlich hinzugefügte Pfade jedoch wieder weg. Sollen sie dauerhaft in der PATH-Variable bleiben, muss der export-Befehl in die .zshrc-Datei geschrieben werden (siehe auch Abschnitt “Die .zshrc-Datei”).

Wird die .zshrc-Datei jedoch mehr neugeladen (source ~/.zshrc), werden zusätzliche Pfade erneut hinzugefügt und kommt somit mehrfach vor. Die kann zu Problemen führen. Dieses Verhalten vermeidet man, indem vorher geprüft wird, ob der Pfad schon vorhanden ist:

# --- Custom PATH entries ------------------------------------------

# Define paths in array
CUSTOM_PATHS=(
	"/Applications/Visual Studio Code.app/Contents/Resources/app/bin"
	# "/add/additional/paths/here" (without comma; semicolon)
)

# Add all paths from CUSTOM_PATHS to PATH if it doesn't exist
for dir in "${CUSTOM_PATHS[@]}"; do
	if [-d "$dir" && ":$PATH:" != *":$dir:"*](-d%20"$dir"%20&&%20":$PATH:"%20!=%20*":$dir:"*.md); then
		export PATH="$PATH:$dir"
	fi
done

# ------------------------------------------------------------------

2.2 ~ vs. $HOME

SymbolBedeutungTypBeispielwert
~Kürzel für das Home-Verzeichnis des aktuellen BenutzersShell-Abkürzung (Shell Expansion)/Users/max
$HOMEUmgebungsvariable, die den Pfad zum Home-Verzeichnis enthältVariable im Shell-Umfeld (Environment Variable)/Users/max

*~ (Tilde):

  • Wird von der Shell (zsh, bash etc.) automatisch expandiert.
  • Funktioniert nur an positionsabhängigen Stellen (z.B. nicht innerhalb von Anführungszeichen “ “).
  • Wird häufig für Navigation verwendet:
cd ~
cd ~/Downloads
ls ~

Die Shell ersetzt ~ intern automatisch durch /Users/<username>.

Sonderformen:

~user  # Home-Verzeichnis eines anderen Users (wenn zugreifbar)
~+     # aktuelles Verzeichnis
~-     # vorheriges Verzeichnis

$HOME:

  • Eine Umgebungsvariable, die den kompletten Pfad enthält.
  • Wird von Programmen, Skripten und Konfigurationsdateien benötigt.
  • Funktioniert auch innerhalb von Strings und Skripten:
echo $HOME
cd $HOME/Documents
export PATH="$PATH:$HOME/bin"

In vielen Programmen ist $HOME die bevorzugte Form, weil sie immer eindeutig ist, auch wenn die Shell keine Tilde-Ersetzung durchführt.

note

Zusammenfassung:

  • ~ = Komfortabler Shell-Shortcut für das persönliche Verzeichnis
  • $HOME = Variable, die das Home-Verzeichnis systemweit speichert
  • Funktional oft identisch, technisch jedoch unterschiedlicher Mechanismus

3 Die .zshrc-Datei

Die Datei .zshrc ist die zentrale Konfigurationsdatei der Z-Shell (zsh). Sie wird bei jedem Start eines neuen interaktiven Terminalfensters automatisch geladen und ausgeführt. Darin lassen sich Befehle, Einstellungen, Umgebungsvariablen und Aliase dauerhaft speichern.

3.1 Speicherort

Die Datei liegt standardmäßig im Home-Verzeichnis des Benutzers:

~/.zshrc

Sollte sie nicht existieren, kann sie wie folgt erstellt werden:

touch ~/.zshrc

3.2 Zweck

In dieser Datei steht alles, was bei einem Terminalstart automatisch ausgeführt werden soll.

TypBeispiel
Aliasealias ll='ls -lah'
Umgebungsvariablenexport PATH="/opt/homebrew/bin:$PATH"
Eigene Funktionengreet() { echo "Hallo $USER"; }
Prompt-AnpassungenPROMPT='%n@%m %1~ %# '
Themen / Plugins (oh-my-zsh)source $ZSH/oh-my-zsh.sh
Autostart von Toolseval "$(brew shellenv)"

3.3 Änderungen übernehmen

Damit Änderungen wirksam werden, muss das Terminal neugestartet oder die Datei neugeladen werden.

Neuladen von ~/.zshrc:

source ~/.zshrc

Nützliches Aliase zum Bearbeiten und Neuladen von ~/.zshrc:

alias editrc="nano ~/.zshrc"
alias reloadrc="source ~/.zshrc"

3.4 Vergleich mit anderen Startdateien

DateiLadezeitpunktBeschreibung
~/.zshrcbei jedem neuen interaktiven Shell-StartHauptdatei für zsh-Konfiguration
~/.zprofilenur Login-Shellsähnlich wie .bash_profile, selten nötig
~/.zloginam Ende einer Login-Shellwird nach .zprofile geladen
~/.zshenvimmer, auch bei nicht-interaktiven Shellsfür globale Umgebungsvariablen
~/.zlogoutbeim Verlassen einer Login-ShellAufräumarbeiten (z. B. Logs, Cache)

note

Die .zshrc ist das Herzstück der Shell-Konfiguration. Alles, was in jedem Terminal automatisch verfügbar sein soll – Aliase, PATH, Funktionen, Themes – gehört in diese Datei.

tip

Verschieben der .zshrc:

Die .zshrc kann wie folgt verschoben und durch einen Symlink ersetzt werden (z. B. wenn sie teil eines Repositories werden soll):

mv ~/.zshrc ~/dotfiles/zshrc
ln -s ~/dotfiles/zshrc ~/.zshrc

Skript einbinden

. ~/.config/zsh/aliases.zsh

Admin-Rechte (sudo)

Ist man als normaler Benutzer angemeldet kann ein Befehl mit Administrator-Rechten ausgeführt werden, indem sudo (superuser do) vorangestellt wird. Da macOS ein UNIX-basiertes System ist, sind viele Systembefehle nur mit Admin-Rechten ausführbar. Sie sind auch erforderlich für den Zugriff auf geschützte Systemdateien und -einstellungen.

1 Syntax

sudo <befehl> [optionen] [argumente]

Bei der ersten Ausführung von sudo wird nach dem Administratorpasswort gefragt.

2 Beispiele

Ordner /usr/local/test mit Administratorrechten erstellen:

sudo mkdir /usr/local/test

Systemverzeichnisse ändern:

sudo nano /etc/hosts

→ Bearbeitung der Host-Datei, um z.  B. Webseiten umzuleiten.

Dienste starten/stoppen:

sudo launchctl load /Library/LaunchDaemons/meindienst.plist

Systemweite Berechtigungen ändern:

sudo chmod 755 /usr/local/bin/meinprogramm

Dateien löschen, die geschützt sind:

sudo rm -rf /Pfad/zur/Datei

caution

Den Befehl sudo sollte man nur verwenden, wenn man genau weiß, was man tut. Eine falsche Verwendung kann das System beschädigen!

3 Root-Shell und Shell mit Root-Rechten

sudo speichert die Authentifizierung kurzfristig (standardmäßig 5 Minuten = 300 Sekunden). Für häufige Admin-Aufgaben eignet sich eine Root-Shell (sudo -i) oder eine Shell mit Root-Rechten (sudo -s).

3.1 Root-Shell (sudo -i = login shell as root)

Es wird simuliert, dass man als Root-Benutzer eingeloggt ist, fast wie bei einem echten Root-Login.

Wichtige Eigenschaften:

  • Es werden die Umgebungsvariablen des Root-Benutzers geladen (PATH, HOME, usw.).
  • Man landet im Home-Verzeichnis des Root-Benutzers (/var/root).
  • Es werden die Shell-Startdateien vom Root-Benutzer genutzt (.profile und .bashrc bzw. .zshrc)
sudo -i
  • Prompt wechselt, z. B. von cgroening@macbook ~ % zu macbook:~ root#
  • echo $HOME zeigt den Ordner /var/root

3.2 Shell mit Root-Rechten (sudo -s = run shell as root)

Man erhält eine Shell mit Root-Rechten.

Aber:

  • Die eigenen Umgebungsvariablen bleiben aktiv.
  • Man verbleibt im aktuellen Benutzerverzeichnis.
  • Die Startdateien des Root-Benutzers werden nicht geladen.
sudo -s
  • Prompt wechselt, z. B. von cgroening@macbook ~ % zu root@macbook ~ #
  • echo $HOME zeigt den Ordner /Users/cgroening

3.3 Zusammenfassung

OptionShelltypVerzeichnisUmgebungs-
variablen
Startdateien
geladen?
Prompt
-Normale ShellBenutzer-Homecgroening@macbook ~ %
sudo -iLogin-ShellRoot-HomeRootJamacbook:~ root#
sudo -sNon-LoginBenutzer-HomeEigeneNeinroot@macbook ~ #

info

sudo -i vs sudo -s

  • Will man komplett wie Root arbeiten, inklusive PATH & HOMEsudo -i
  • Willst man nur temporär Root-Rechte, aber mit der eigenen Umgebung → sudo -s

4 Übersicht

BefehlFunktion
sudo <befehl>Führt einen Befehl als Administrator aus
sudo -iÖffnet eine Root-Shell
sudo -sÖffnet Shell mit Root-Rechten
sudo !!Wiederholt letzten Befehl mit sudo
sudo -vErneuert den sudo-Timer, ohne einen Befehl auszuführen
sudo -kLöscht sofort alle gespeicherten Authentifizierungen
exitVerlässt die Root-Shell bzw. Shell mit Root-Rechten

Berechtigungen (chmod) und Besitz (chown)

1 chmod

1.1 Rechte und Benutzergruppen

chmod steht für change mode. Mit diesem Befehl werden Datei- und Verzeichnisrechte geändert.

Es gibt drei Arten von Rechten:

  1. r: read (lesen)
  2. w: write (schreiben)
  3. x: execute (ausführen)

Man unterscheidet drei Benutzergruppen:

  1. u: user (Besitzer)
  2. g: group (Gruppe)
  3. o: others (alle anderen)
  • a: all (alle drei Gruppen: u+g+o)

1.2 Schreibweisen

1.2.1 Symbolische Schreibweise

Es werden Buchstaben und Operatoren verwendet:

  • +: Recht hinzufügen
  • -: Recht entfernen
  • =: Rechte gleichsetzen

Beispiele:

chmod u+x datei.sh    # Besitzer darf die Datei ausführen
chmod g-w datei.txt   # Gruppe darf nicht mehr schreiben
chmod o=r datei.txt   # Andere dürfen nur lesen
chmod a+r datei.txt   # Alle dürfen lesen

1.2.2 Oktal-/Numerische Schreibweise

Jedes Recht hat einen Zahlenwert:

RechtWert
r4
w2
x1

Man addiert die Werte für jede Gruppe um Rechte zu setzen:

---    →    0+0+0 = 0    (keine Rechte)
--x    →    0+0+1 = 1    (nur ausführen)
-w-    →    0+2+0 = 2    (nur schreiben)
-wx    →    0+2+1 = 3    (schreiben + ausführen)
r--    →    4+0+0 = 4    (nur lesen)
r-x    →    4+0+1 = 5    (lesen + ausführen)
rw-    →    4+2+0 = 6    (lesen + schreiben)
rwx    →    4+2+1 = 7    (lesen + schreiben + ausführen)

Beispiel:

chmod 755 datei.sh

Bedeutung:

  • 7 (u)rwx → Besitzer darf alles
  • 5 (g)r-x → Gruppe darf lesen & ausführen
  • 5 (o)r-x → andere dürfen lesen & ausführen

1.3 Nützliche Operationen

-R → rekursiv (für Verzeichnisse):

chmod -R 755 /pfad/zum/verzeichnis

--reference=DATEI → Rechte von einer anderen Datei kopieren:

chmod --reference=vorlage.txt ziel.txt

-v → verbose, zeigt was geändert wurde:

chmod -v 644 datei.txt

2 chown

chown steht für change owner. Dieser Befehl ändern den Besitzer oder die Gruppe einer Datei bzw. eines Verzeichnisses.

Jede Datei hat

  • einen user (Besitzer) und
  • eine group (Gruppe).

2.1 Syntax

chown [OPTIONEN] BESITZER[:GRUPPE] DATEI/VERZEICHNIS
  • BESITZER: neuer Benutzer, der die Datei besitzen soll
  • GRUPPE: neue Gruppe, der die Datei angehören soll
  • : → trennt Besitzer und Gruppe

info

Nur Root oder der Besitzer selbst kann chown ausführen. Normale Nutzer können nicht einfach Dateien anderen Nutzern zuweisen. In dem Fall muss der Befehl mit Admin-Rechten ausgeführt werden:

sudo chown ...

Beispiele:

chown anna datei.txt             # Ändert den Besitzer auf anna
chown :developers datei.txt      # Ändert die Gruppe auf developers
chown anna:developers datei.txt  # Ändert Besitzer auf anna und Gruppe auf developers

2.2 Wichtige Optionen

OptionBedeutung
-Rrekursiv, für Verzeichnisse und deren Inhalte
-vverbose, zeigt Änderungen an
--reference=DATEIsetzt Besitzer/Gruppe wie bei einer Referenzdatei

Beispiel für Rekursiv:

chown -R anna:developers /home/anna/projekt

Alle Dateien und Unterverzeichnisse gehören danach dem Benutzer anna und der Gruppe developers.

Beispiel für --reference:

chown --reference=vorlage.txt ziel.txt

Besitzer & Gruppe wie bei vorlage.txt gesetzt.

note

Die Standardgruppe für normale Benutzer (nicht für Systemprozesse) lautet staff.

3 ACL

ACL steht für Access Control List (Zugriffssteuerungsliste). Sie erweitert das klassische UNIX-Berechtigungssystem (Lesen, Schreiben, Ausführen für Benutzer / Gruppe / andere) um feinere Zugriffsrechte.

Während das herkömmliche System nur drei Berechtigungsebenen kennt (z. B. rw-r--r--), kann eine ACL mehreren einzelnen Benutzern oder Gruppen ganz spezifische Rechte zuweisen.

note

ACLs sind eine erweiterte Form von Dateiberechtigungen, mit denen man mehr Kontrolle darüber hat, wer was mit einer Datei tun darf.

Beispiel:

Angenommen man hat die folgende Datei:

-rw-r--r--  user  staff  dokument.txt

Es darf nur user schreiben, alle anderen dürfen nur lesen. Mit einer ACL kann man z. B. einem zusätzlichen andern Benutzer wie john Schreibrechte geben:

chmod +a "alex allow write" dokument.txt

Der Befehl ls -le liefert dadurch die folgende Ausgabe:

-rw-r--r--+  user  staff  dokument.txt
  0: john allow write

Das Pluszeichen (+) hinter den Berechtigungen zeigt an, dass eine ACL vorhanden ist.

3.1 Typische Befehle

BefehlBedeutung
ls -leZeigt ACL-Einträge für Dateien und Ordner
chmod +a "user allow permission"Fügt einen ACL-Eintrag hinzu
chmod -a "user"Entfernt einen ACL-Eintrag
chmod -N dateiEntfernt alle ACLs von einer Datei

Aliase, Pipes, Weiterleitungen und Verkettungen

1 Aliase

Ein Alias ist ein benutzerdefinierter Kurzbefehl. Er ersetzt längere und/oder häufig genutzt Terminal befehle durch ein Kürzel.

1.1 Typische Aliase (Beispiele)

alias ll="ls -lah"
alias la="ls -A"
alias gs="git status"
alias gp="git pull"
alias v="nvim"
alias ..="cd .."
alias ...="cd ../.."

Es sind auch dynamische Aliase möglich:

alias now='date "+%Y-%m-%d %H:%M:%S"'
alias weather='curl wttr.in'

1.2 Dauerhafte Speicherung von Aliasen

Damit die Aliase bei jedem Start verfügbar sind, müssen sie in die ~/.zshrc eingefügt werden (siehe auch Abschnitt “Die .zshrc-Datei”).

Öffnen von ~/.zshrc:

nano ~/.zshrc

Anschließend zum Beispiel:

# Eigene Aliase
alias editrc="nano ~/.zshrc"
alias reloadrc="source ~/.zshrc"
alias openfl="open -a ForkLift ."

Neuladen:

source ~/.zshrc

2 Pipes (|) – Kombination von Kommandos

Mit dem Pipe-Zeichen (|) wird die Ausgabe eines Befehls als Eingabe für den nächsten Befehl weitergeleitet. Auf diese Weise kann man kleine Werkzeuge zu sehr mächtigen Befehlen kombinieren.

2.1 Beispiele

Alle Dateien auflisten und nach Dateiendung .txt filtern:

ls | grep txt
  • ls: listet alle Dateien und Ordner im aktuellen Verzeichnis auf
  • grep txt: filtert die Ausgabe und zeigt nur Zeilen an, die „txt“ enthalten (d. h. .txt-Dateien)

Prozesse anzeigen, die “Safari” enthalten:

ps aux | grep Safari
  • ps aux: zeigt alle laufenden Prozesse im System mit Benutzer, PID, CPU- und Speicherverbrauch
  • grep Safari: filtert die Prozessliste und zeigt nur Zeilen, in denen “Safari” vorkommt

Zählen, wie viele Zeilen im Log das Wort “ERROR” enthalten:

cat access.log | grep "ERROR" | wc -l
  • cat access.log: gibt den gesamten Inhalt der Datei access.log aus
  • grep "ERROR": filtert alle Zeilen, die das Wort „ERROR“ enthalten
  • wc -l: zählt die Anzahl der Zeilen der gefilterten Ausgabe (d. h. die Anzahl der Fehlermeldungen)

Pipes können beliebig verkettet werden. Somit sind sie in Kombination mit grep, awk, sed, sort, uniq, cut usw. sehr mächtig.

2.2 Relevante Befehle für Pipes

BefehlBeschreibungBeispiel
grepFiltert Zeilen nach Textmustern; unterstützt reguläre Ausdrückedmesg | grep error
awkVerarbeitet Textzeilen spaltenweise, ermöglicht Filter, Formatierungen und erlaubt Berechnungenls -l | awk '{print $9, $5}'
sedStream-Editor zum Ersetzen und Bearbeiten von Text innerhalb eines Datenstromscat file.txt | sed 's/foo/bar/g'
sortSortiert Zeilen alphabetisch oder numerisch.cat names.txt | sort
uniqEntfernt doppelte Zeilen (oft nach sort verwendet)sort file.txt | uniq
cutSchneidet Spalten aus Textzeilen heraus (z. B. bei CSV-Dateien).cat users.csv | cut -d',' -f1

2.2.1 grep

Durchsucht Textzeilen nach Mustern oder Wörtern. Unterstützt reguläre Ausdrücke.

Syntax:

grep [Optionen] Muster [Datei(en)]

Häufige Optionen:

  • -i: Groß-/Kleinschreibung ignorieren
  • -r: rekursiv durchsuchen
  • -n: Zeilennummern anzeigen

Beispiele:

grep "error" /var/log/system.log
ps aux | grep Safari

Siehe auch grep-Beispiele.

2.2.2 awk

Zeilen- und spaltenorientierter Textprozessor, ideal für Auswertungen oder Filterung.

Syntax:

awk '{Aktion}' [Datei]

Beispiele:

ls -l | awk '{print $9, $5}'      # Dateiname + Größe
df -h | awk '{print $1, $5}'      # Laufwerk + Auslastung

2.2.3 sed

Stream-Editor zum automatischen Bearbeiten von Texten in Datenströmen.

Syntax:

sed 's/ALT/NEU/g' [Datei]

Häufige Optionen:

  • -i: Änderungen direkt in der Datei speichern
  • -n: unterdrückt Standardausgabe

Beispiele:

cat config.txt | sed 's/enabled=false/enabled=true/g'
sed -i '' 's/http:/https:/g' urls.txt

2.2.4 sort

Sortiert Zeilen alphabetisch oder numerisch.

Syntax:

sort [Optionen] [Datei]

Syntax:

  • -n: numerisch sortieren
  • -r: Reihenfolge umkehren

Beispiele:

cat names.txt | sort
ls -l | sort -k5 -n

2.2.5 uniq

Entfernt aufeinanderfolgende Duplikate aus sortierten Listen.

Syntax:

uniq [Optionen] [Datei]

Häufige Optionen:

  • -c: zählt Vorkommen
  • -d: zeigt nur doppelte Einträge

Beispiele:

sort names.txt | uniq
sort names.txt | uniq -c

2.2.6 cut

Extrahiert Spalten aus Textzeilen – besonders nützlich bei CSV-Dateien.

Syntax:

cut -d[Trennzeichen] -f[Feldnummer] [Datei]

Häufige Optionen:

  • -d: legt das Trennzeichen fest (z. B. , oder :)
  • -f: bestimmt die Spaltennummer(n)

Beispiele:

cat users.csv | cut -d',' -f1     # erste Spalte (Name)
ps aux | cut -c1-10               # Zeichen 1–10 jeder Zeile

3 Weiterleitungen (>, >>, <)

Weiterleitungen bestimmen, wohin die Eingaben oder Ausgaben eines Befehls gehen.

3.1 Arten von Weiterleitungen

SymbolBedeutungBeispiel
>Ausgabe in Datei schreiben (überschreibt)ls > dateien.txt
>>Ausgabe anhängenecho "neuer Eintrag" >> log.txt
<Eingabe aus Datei lesensort < unsortiert.txt
2>Fehlermeldungen in Datei schreibenls xyz 2> fehler.txt
&>Ausgabe und Fehler umleitenscript.sh &> output.txt

3.2 Beispiele

Die Namen aller JPEG-Dateien in eine Textdatei schreiben:

find . -name "*.jpg" > bilder.txt

Gefundene Warnungen aus einem Log an eine Textdatei anhängen:

grep "WARN" log.txt >> warnungen.txt

Aus input.txt lesen und in output.txt schreiben:

cat < input.txt > output.txt

Stumme Ausführung eines Befehls (keine Ausgabe, d. h. keine sichtbare Fehlermeldung):

befehl &> /dev/null

4 Verkettung von Befehlen – AND (&&) und OR (||)

mkdir ordner123 && cd ordner123  # Ordner erstellen und hineinwechseln
cd ordner123 || mkdir ordner123  # In Ordner wechseln oder erstellen, wenn er nicht existiert

Packetmanagement mit Homebrew

Das Software-Installationssystem Homebrew bezeichnet sich selbst als “The Missing Package Manager for macOS (or Linux)” und ist unter macOS der inoffizielle Standard für die Installation von Software über das Terminal. Homebrew macht die Kommandozeile zu einer echten Linux-ähnlichen Umgebung. Es verwaltet Programme, Bibliotheken und Entwicklerwerkzeuge – alles über einfache Befehle.

Installation von Homebrew:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Überprüfung, ob alles korrekt eingerichtet ist:

brew doctor

Wesentliche Vorteile von Homebrew:

  • Sehr schnelle Installation von Software (deutlich schneller als manuell)
  • Ermöglicht ein konsistentes Setup über mehrere Rechner hinweg
  • Hat eine riesige Community und Tausende Pakete

1 Grundlegende Befehle

BefehlBeschreibung
brew install <paket>Installiert ein Paket
brew uninstall <paket>Entfernt ein Paket
brew listZeigt installierte Pakete
brew updateAktualisiert Homebrew selbst
brew upgradeAktualisiert installierte Pakete
brew search <suchbegriff>Sucht nach Paketen
brew info <paket>Zeigt Infos, Version, Pfad etc.
brew cleanupLöscht alte Versionen und Cache

GUI-Apps über Homebrew Cask:

brew install --cask visual-studio-code
brew install --cask google-chrome
brew install --cask rectangle

--cask = für Anwendungen mit Benutzeroberfläche.

2 Paketpfade & Umgebungsvariablen

Homebrew installiert standardmäßig nach:

  • /usr/local (bei Intel-Macs)
  • /opt/homebrew (bei Apple Silicon)

Über eine Umgebungsvariable in der ~/.zshrc kann man den Installationspfad anpassen:

export PATH="/opt/homebrew/bin:$PATH"

3 Backup und Wiederherstellung

Liste aller installierten Pakete erstellen:

brew bundle dump --file=~/brewfile.txt

Um die vorhandene Datei zu überschreiben:

brew bundle dump --file=~/brewfile.txt --force

Wiederherstellung der Installation:

brew bundle --file=~/brewfile.txt

4 Kombinationsbeispiel: Homebrew + Alias + Pipe

Auf veraltete Pakete prüfen:

alias brewoutdated="brew outdated | grep -vE 'python|node'"

Automatisches Aufräumen:

alias brewclean="brew cleanup && brew autoremove && brew doctor"

Tipps und Tools für maximale Effizienz

1 Kurzbefehle im Terminal

1.1 Navigation und Bearbeitung

KurzbefehlBeschreibung
STRG + ASpringt zum Zeilenanfang
STRG + ESpringt zum Zeilenende
OPTION + BSpringt ein Wort zurück
OPTION + FSpringt ein Wort vorwärts
STRG + ULöscht alles vor dem Cursor
STRG + KLöscht alles nach dem Cursor
STRG + WLöscht das Wort vor dem Cursor
OPTION + DLöscht das Wort nach dem Cursor
STRG + YFügt zuletzt gelöschten Text ein (yank)
STRG + LBildschirm löschen (wie clear)
STRG + CBeendet den laufenden Prozess
STRG + ZHält den Prozess an (siehe Job-Control)
STRG + DBeendet die Shell / EOF senden

1.2 History-Navigation

KurzbefehlBeschreibung
↑ / ↓Vorherige/nächste Befehle durchsuchen
STRG + RRückwärts-Suche in der History
STRG + SVorwärts-Suche in der History
STRG + GSuche abbrechen
!!Letzten Befehl wiederholen
!$Letztes Argument des letzten Befehls
!^Erstes Argument des letzten Befehls
!*Alle Argumente des letzten Befehls

1.3 Tab-Vervollständigung

KurzbefehlBeschreibung
TABBefehl/Pfad vervollständigen
TAB TABAlle Möglichkeiten anzeigen
OPTION + /Pfad-Vervollständigung erzwingen

1.4 Praktische Kombinationen

Letzten Befehl mit sudo wiederholen:

apt update
# Permission denied
sudo !!

Argument vom letzten Befehl übernehmen:

cat /etc/hosts
vim !$    # öffnet /etc/hosts in vim

In der History suchen:

# STRG + R drücken, dann tippen:
(reverse-i-search)`brew': brew install htop

2 Dokumentation

2.1 man – Handbuch zu beliebigem Befehl anzeigen

man grep

Navigation im Handbuch:

  • Leertaste: Nächste Seite
  • b: Vorherige Seite
  • /: Vorwärts suchen
  • ?: Rückwärts suchen
  • n: Nächstes Suchergebnis
  • N: Vorheriges Suchergebnis
  • q: Beenden

Manpage-Sektionen:

man 1 printf    # Benutzerkommandos
man 2 open      # Systemaufrufe
man 3 printf    # Bibliotheksfunktionen

2.2 whatis – Kurzbeschreibung

Zeigt eine einzeilige Beschreibung eines Befehls:

whatis ls
# ls(1) - list directory contents

whatis grep sed awk
# grep(1) - file pattern searcher
# sed(1) - stream editor
# awk(1) - pattern-directed scanning and processing language

2.3 apropos – Befehle suchen

Durchsucht die Manpage-Datenbank nach Stichwörtern:

apropos network
apropos "copy files"
apropos -s 1 search    # Nur in Sektion 1 suchen

Nützlich, wenn man nicht genau weiß, welcher Befehl benötigt wird.

2.4 tldr – Vereinfachte Hilfe mit Beispielen

tldr (Too Long; Didn’t Read) bietet prägnante, praxisorientierte Hilfe mit Beispielen.

Installation:

brew install tldr

Verwendung:

tldr tar
tldr find
tldr git-commit

Beispielausgabe für tar:

tar

Archiving utility.
Often combined with a compression method, such as gzip or bzip2.
More information: https://www.gnu.org/software/tar.

- Create an archive from files:
  tar cf target.tar file1 file2 file3

- Create a gzipped archive:
  tar czf target.tar.gz file1 file2 file3

- Extract an archive:
  tar xf source.tar

Cache aktualisieren:

tldr --update

2.5 Vergleich der Dokumentations-Tools

ToolZweckDetailgradBeispiele
manVollständige ReferenzSehr hochWenige
whatisKurzbeschreibungMinimalKeine
aproposBefehl finden--
tldrSchnelle HilfeMittelViele

3 Empfohlener Workflow

  1. Unbekannter Befehl: tldr <befehl> für schnelle Orientierung
  2. Befehl finden: apropos <stichwort> zum Suchen
  3. Details nachschlagen: man <befehl> für vollständige Dokumentation
  4. Kurze Info: whatis <befehl> für Einzeiler

4 Finder

Aktuellen Ordner oder ausgewählte Datei im Finder öffnen:

  • Rechtsklick auf Ordner oder Datei in Pfadleiste :luc_arrow_right: “In Terminal öffnen”
  • oder: Rechtsklick auf Ordner oder Datei in Pfadleiste :luc_arrow_right: “Dienste” :luc_arrow_right: “New Warp Tab Here”

5 tee-Befehl

Der tee-Befehl funktioniert als “T-Stück” für Datenströme. Er dupliziert die Ausgabe eines Befehls und sendet diese

  • ins Terminal (stdout) und
  • an eine andere Ziel (Datei, Pipe, usw.).

Dies ist besonders hilfreich bei Aliasen, Shell-Funktionen und Skripten.

Beispiel:

alias copypwd='pwd | tee >(pbcopy)'

Der aktuelle Pfad wird in die Zwischenablage kopiert und im Terminal ausgegeben. Mit pwd | pbcopy, d. h. ohne tee, wird nur der Pfad kopiert, aber nicht ausgegeben.

6 Ausführung von AppleScript

AppleScript- oder JXA-Code (JXA = JavaScript for Automation) kann mit dem Befehl osascript ausgeführt werden.

Syntax:

osascript [Optionen] [Datei | -e "Skript-Code"]

Ein Skript aus einer Datei ausführen:

osascript ~/Skripte/mein_skript.scpt

Beispiel:

Anzeige des Titels des aktuellen Songs aus Apple Music:

osascript -e 'tell application "Music" to get name of current track'

Optionen:

  • -e: Führe eine oder mehrere Codezeilen direkt aus.

  • -l JavaScript: Verwende JavaScript statt AppleScript

    Beispiel: osascript -l JavaScript -e 'Application("Finder").open("/Applications")'

6.1 Mitteilungen

Mittels Apple Script ist es auch möglich, Mitteilungen, die rechts oben auf dem Bildschirm erscheinen, auszugeben:

osascript -e 'display notification "Hallo!"'

6.2 Typische Anwendungsfälle

  • Automatisierung von Aufgaben von macOS-Apps (z. B. Fenster öffnen, Dateien verschieben)
  • Systemdialoge oder Benachrichtigungen aus Skripten heraus anzeigen
  • Kombination von Terminal-Befehle mit macOS-Funktionen (z. B. GUI-Interaktionen)
  • Integration in Shell-Skripte und Workflows

7 Brace Expansion – Effiziente Musterbildung

Brace Expansion ist ein mächtiges Shell-Feature zum schnellen Erzeugen von Mustern und Sequenzen.

7.1 Zahlensequenzen

echo {1..10}              # 1 2 3 4 5 6 7 8 9 10
echo {01..10}             # 01 02 03 04 05 06 07 08 09 10
echo {10..1}              # Rückwärts: 10 9 8 7 6 5 4 3 2 1
echo {a..z}               # Buchstaben: a b c d ... z
echo {2..20..2}           # Schrittweite: 2 4 6 8 10 12 14 16 18 20

7.2 Listen

echo {rot,grün,blau}      # rot grün blau
mkdir {Docs,Images,Videos}

7.3 Kombinationen

echo {A,B}{1,2}           # A1 A2 B1 B2
mkdir Jahr_{2023,2024}/{Q1,Q2,Q3,Q4}

7.4 Praktische Anwendungen

Backup erstellen:

cp wichtig.txt{,.backup}  # Kopiert nach wichtig.txt.backup

Mehrere Dateien auf einmal umbenennen:

mv bild.{jpg,png}         # Umbenennen von jpg zu png

Batch-Download:

curl https://example.com/file{001..100}.jpg -O

Projektstruktur anlegen:

mkdir -p projekt/{src,tests,docs}/{backend,frontend}

Beispiele für alltägliche Aufgaben im Terminal

1 Dateien suchen

Im aktuellen Ordner sollen alle Dateien vom Typ PDF gefunden werden.

1.1 GUI

  • Finder öffnen :luc_arrow_right: im Eingabefeld für die Suche rechts oben “.pdf” eingeben
  • Weitere Verfeinerungen über Filter möglich (+-Button)

1.2 Terminal

find . -name "*.pdf"

:luc_arrow_right: Listet alle PDF-Dateien (auch in Unterordnern)

Bis hier ist der Aufwand gleich. Das Terminal ist jedoch deutlich schneller, wenn zusätzlich alle Dateien gefunden werden sollen, die einen bestimmten Dateiinhalt haben.

Beispiel:

find . -name "*.pdf" -exec grep -Hn "Rechnung" {} \

2 Zuletzt bearbeitete Dateien anzeigen

Es sollen alle JPEG-Dateien eines Ordners angezeigt werden, die in den letzten 10 Minuten bearbeitet wurden. Anschließend sollen diese in der Vorschau-App geöffnet werden.

2.1 GUI

  1. Im Finder in die Listenansicht wechseln und die Spalte “Änderungsdatum” anzeigen lassen, falls noch nicht vorhanden
  2. Auf die Spaltenüberschrift klicken, um nach Änderungsdatum zu sortieren
  3. Die Datei mit dem ältesten Änderungsdatum, das in Frage kommt, suchen
  4. Auswählen von dieser Datei und allen, die sich davor/dahinter befinden
  5. Rechtsklick :luc_arrow_right: Öffnen mit :luc_arrow_right: Vorschau.app
  6. Zurück zum Finder wechseln und alle ursprünglichen Darstellungseinstellungen wiederherstellen

2.2 Terminal

Alle JPEG-Dateien anzeigen, die nicht älter als 10 Minuten sind:

find . -type f -iname "*.jpeg" -mmin -10

Sollen Dateien mit den Endungen .jpg und .jpeg angezeigt werden, muss der Befehl wie folgt erweitert werden:

find . -type f \( -iname "*.jpg" -o -iname "*.jpeg" \) -mmin -10

Genau diese Dateien in der Vorschau-App öffnen:

find . -type f -iname "*.jpeg" -mmin -10 -print0 | xargs -0 open -a Preview

2.2.1 Alias

Besonders schnell und einfach erfolgt es, wenn man sich einen Alias erstellt:

alias showrecent='find . -type f -iname "*.jpg" -mmin $1'

Komfortabler ist es in diesem Fall, wenn man stattdessen Funktionen erstellt:

showrecent () {
	echo "The following files were modified in the past $1 minutes:"
	find . -type f -iname "*.*" -mmin -$1
}

openrecentjpg () {
	echo "Opening the following files in preview:"
	find . -type f \( -iname "*.jpg" -o -iname "*.jpeg" \) -mmin -$1
	find . -type f \( -iname "*.jpg" -o -iname "*.jpeg" \) -mmin -$1 -print0 | xargs -0 open -a Preview
}

Verwendung:

showrecent 10
openrecentjpg 10

Beispielausgabe:

ls

IMG_7094.jpeg		IMG_7096 Kopie.jpeg	IMG_7097.jpeg		IMG_7099.jpeg
IMG_7095.jpeg		IMG_7096.jpeg		IMG_7099 Kopie.jpeg
showrecent 10

The following files were modified in the past 10 minutes:
./IMG_7099 Kopie.jpeg
./IMG_7096 Kopie.jpeg
openrecentjpg 10

Opening the following files in preview:
./IMG_7099 Kopie.jpeg
./IMG_7096 Kopie.jpeg

tip

Sollte ein Funktionsname verwendet werden sollen, der zuvor ein Alias war (z. B. alias showrecent=... :luc_arrow_right: showrecent() ...) muss zuvor der folgende Befehl ausgeführt werden:

unalias <aliasname>

Shell-Scripting

1 Motivation & Abgrenzung: Shell-Scripting vs. Python

Auch wenn man Python beherrscht, lohnt es sich, Shell-Scripting zu lernen. Beide haben unterschiedliche Stärken und Einsatzzwecke und werden in der Praxis sehr häufig kombiniert, statt sich gegenseitig zu ersetzen.

Grundlegende Unterschiede:

ThemaShell-ScriptingPython
ZweckAutomatisierung von System- und TerminalaufgabenUniverselle Programmiersprache für komplexe Logik
StartzeitExtrem schnell, sofort verfügbarInterpreter-Start kostet mehr Zeit
VerfügbarkeitÜberall vorinstalliert (Unix, Linux, macOS)Nicht immer vorinstalliert (abhängig vom System)
Datei- und SystemsteuerungSehr direkt, ideal für kleine AutomatisierungenGut, aber oft umfangreichere Syntax/Module nötig
TextverarbeitungÜberragend durch sed, awk, grep, PipesMöglich, aber weniger kompakt
Komplexe ProgrammeSchwer wartbar bei großer LogikOptimale Wahl für komplexe Software
Konfiguration / DevOpsStandard für Deployment, Cronjobs, Server-SkripteStandard für Automatisierung, APIs, Datenverarbeitung

2 was kann shell-scripting besser als python?

2.1 1. Schnelle Systembefehle und Administrative Tasks:

Beispiel: fünf Dateien verschieben und einen Dienst neu starten:

mv *.log /backup/logs && systemctl restart apache2

In Python ist es deutlich umständlicher:

import glob
import shutil
import subprocess

# Find all .log files
for file in glob.glob('*.log'):
    shutil.move(file, '/backup/logs')

# Restart service
result = subprocess.run(
    ['systemctl', 'restart', 'apache2'], capture_output=True, text=True
)

if result.returncode == 0:
    print('Service started.')
else:
    print('Error: ', result.stderr)

Hier kann man klar sehen, dass Shell-Scripting bei einfachen Systemaufgaben schneller und präziser ist, während Python mehr Kontrolle und Fehlerbehandlung ermöglicht, aber auch mehr Code erfordert.

2.1.1 2. Pipelines und Stream-Verarbeitung:

Shell + Tools sind darauf optimiert, Streams zu verarbeiten, z. B.:

ps aux | grep python | awk '{print $2}' | xargs kill

In Python müsste man Prozesse einlesen, parsen, filtern, Subprozesse starten …

2.1.2 3. Automatisierung in Systemstart, Cron, Deployment:

  • cronjobs
  • launchd
  • systemd
  • Installationsskripte (install.sh sind Standard)

2.1.3 4. Universelle Verfügbarkeit

Shell steht auf jedem Server bereit. Python nicht immer.

Shell-Scripting ist

  • essentiell für DevOps, Sysadmins, Server & Deployment;
  • perfekt für kleine Tools, Setup-Skripte und wiederkehrende Aufgaben;
  • unverzichtbar für CI/CD & Cloud und
  • enorm zeitsparend bei Routine-Aufgaben.

2.2 Was kann Python besser als Shell?

Python ist ideal für:

  • komplexe Logik & Algorithmen
  • APIs & Webservices
  • Datenanalyse & Machine Learning
  • GUI-Tools
  • saubere Tests & Wartbarkeit

Beispiel: JSON-Parsing in Python:

import json
data = json.loads(json_string)

In Shell wäre das schwierig und fehleranfälliger. Es ist jq erforderlich (Kommandozeilenwerkzeug zur Verarbeitung von JSON-Daten).

2.3 Warum beides kombinieren?

Man muss sich nicht entscheiden: Häufig wird beides verwendet ⇒ Best-of-Both-Worlds.

Python aus einem Shell-Script heraus aufrufen:

#!/bin/zsh
echo "Starte Datenverarbeitung..."
python3 analyse.py input.csv
echo "Fertig!"

Shell aus Python heraus verwenden:

import subprocess
subprocess.run(["ls", "-la"])

Python als Tool + Shell als Wrapper (häufigste Variante):

#!/bin/zsh
INPUT=$1
python3 process.py "$INPUT" | tee results.txt
  • Shell übernimmt Dateihandling und Prozesslogik
  • Python rechnet, verarbeitet, analysiert

2.4 Zusammenfassung

Es ist wie ein Schraubendrehen vs. Akkuschrauber:

  • Beides sind Werkzeuge.
  • Shell = leicht, schnell, überall verfügbar
  • Python = leistungsstark, modular, bei großen Aufgaben überlegen

Empfehlung:

  • Shell für kleine Automatisierungen (10–50 Zeilen)
  • Python für komplexe Logik, APIs, Datenverarbeitung, ML
  • Beides kombinieren für professionelle DevOps-Workflows

3 Grundlagen des Shell-Scripting

Ein Shell-Skript ist eine Textdatei mit einer Folge von Befehlen, die nacheinander ausgeführt werden. Skripte ermöglichen die Automatisierung wiederkehrender Aufgaben und können beliebig komplex werden.

Minimales Beispiel:

#!/bin/zsh
echo "Hallo Welt"

3.1 Aliase vs. Funktionen

Sowohl Aliase als auch Funktionen dienen dazu, häufig verwendete Befehle abzukürzen. Sie unterscheiden sich jedoch in ihren Möglichkeiten.

Aliase:

Ein Alias ist eine einfache Textersetzung. Er eignet sich für kurze, statische Befehlsketten.

# Definition
alias ll="ls -lah"
alias gs="git status"
alias ..="cd .."

# Verwendung
ll
gs
..

Einschränkungen von Aliasen:

  • Keine Argumente an beliebiger Stelle möglich
  • Keine Kontrollstrukturen (if, for, while)
  • Keine lokalen Variablen
# Das funktioniert NICHT wie erwartet:
alias greet="echo Hallo $1"  # $1 wird sofort ausgewertet, nicht beim Aufruf

Funktionen:

Funktionen sind flexibler und ermöglichen komplexere Logik mit Argumenten, Variablen und Kontrollstrukturen.

# Definition
greet() {
    echo "Hallo $1!"
}

# Verwendung
greet "Welt"    # Ausgabe: Hallo Welt!
greet "Max"     # Ausgabe: Hallo Max!

Beispiel mit mehreren Argumenten:

mkcd() {
    mkdir -p "$1" && cd "$1"
}

# Verwendung
mkcd neuer_ordner  # Erstellt Ordner und wechselt hinein

Beispiel mit Kontrollstruktur:

backup() {
    if [-z "$1"](-z%20"$1".md); then
        echo "Fehler: Kein Dateiname angegeben"
        return 1
    fi
    cp "$1" "$1.bak"
    echo "Backup erstellt: $1.bak"
}

Vergleich:

MerkmalAliasFunktion
Argumente verarbeiten❌ Nein✅ Ja ($1, $2, …)
Kontrollstrukturen❌ Nein✅ Ja
Lokale Variablen❌ Nein✅ Ja (local)
Mehrzeilige Logik❌ Nein✅ Ja
Rückgabewerte❌ Nein✅ Ja (return)
Einfache Ersetzung✅ IdealÜberdimensioniert

Faustregel:

  • Alias: Für einfache Abkürzungen ohne Argumente
  • Funktion: Sobald Argumente, Logik oder Variablen benötigt werden

Speicherort:

Beide werden typischerweise in ~/.zshrc definiert. Bei vielen Aliasen und Funktionen empfiehlt sich eine Auslagerung:

# In ~/.zshrc
source ~/.config/zsh/aliases.zsh
source ~/.config/zsh/functions.zsh

Einbindung in .zshrc:

SCRIPT_DIR="/parent/folder/of/zshrc"
source "$SCRIPT_DIR/aliases.zsh"
source "$SCRIPT_DIR/functions.zsh"

4 Variablen

Variablen speichern Werte, die im Skript wiederverwendet werden können.

4.1 Variablenzuweisung

warning

Bei der Zuweisung von Werten, dürfen sich keine Leerzeichen um das = befinden!

# Richtig
name="Max"
zahl=42
pfad="/Users/max/Documents"

# FALSCH – führt zu Fehlern
name = "Max"    # Fehler: "name" wird als Befehl interpretiert

Variablen verwenden:

name="Max"
echo "Hallo $name"           # Ausgabe: Hallo Max
echo "Hallo ${name}!"        # Ausgabe: Hallo Max!
echo "Pfad: ${name}_backup"  # Geschweifte Klammern bei Verkettung

Befehlsausgabe in Variable speichern:

# Moderne Syntax (empfohlen)
datum=$(date +%Y-%m-%d)
dateien=$(ls -1 | wc -l)

# Alte Syntax (Backticks) – funktioniert, aber weniger lesbar
datum=`date +%Y-%m-%d`

echo "Heute ist $datum"
echo "Anzahl Dateien: $dateien"

Rechnen mit Variablen:

a=5
b=3

# Arithmetische Auswertung
summe=$((a + b))
produkt=$((a * b))

echo "Summe: $summe"       # Ausgabe: Summe: 8
echo "Produkt: $produkt"   # Ausgabe: Produkt: 15

4.2 Environment-Variablen vs. lokale Variablen

Lokale Variablen:

Lokale Variablen existieren nur in der aktuellen Shell oder Funktion. Sie werden nicht an Kindprozesse vererbt.

meine_var="lokal"
echo $meine_var  # Funktioniert

# In einem Subprozess:
bash -c 'echo $meine_var'  # Ausgabe: (leer)

Environment-Variablen (exportiert):

Mit export wird eine Variable zur Environment-Variable. Sie wird an alle Kindprozesse vererbt.

export MEINE_VAR="global"
echo $MEINE_VAR  # Funktioniert

# In einem Subprozess:
bash -c 'echo $MEINE_VAR'  # Ausgabe: global

Wichtige System-Environment-Variablen:

VariableBeschreibung
$HOMEHome-Verzeichnis des Benutzers
$PATHSuchpfade für ausführbare Programme
$USERAktueller Benutzername
$SHELLPfad zur aktuellen Shell
$PWDAktuelles Arbeitsverzeichnis
$OLDPWDVorheriges Verzeichnis
$EDITORStandard-Texteditor
$LANGSpracheinstellung

Alle Environment-Variablen anzeigen:

env
printenv

Lokale Variablen in Funktionen:

Mit local wird eine Variable auf die Funktion beschränkt:

test_funktion() {
    local lokale_var="nur hier sichtbar"
    globale_var="überall sichtbar"
    echo "In Funktion: $lokale_var"
}

test_funktion
echo "Außerhalb: $lokale_var"   # Ausgabe: (leer)
echo "Außerhalb: $globale_var"  # Ausgabe: überall sichtbar

5 Strings

5.1 Strings über mehrere Zeilen

Methode 1: Here-Document (heredoc):

cat << EOF
Dies ist ein mehrzeiliger Text.
Er kann Variablen enthalten: $USER
Und mehrere Zeilen umfassen.
EOF

In Variable speichern:

text=$(cat << EOF
Zeile 1
Zeile 2
Zeile 3
EOF
)

echo "$text"

Ohne Variablenexpansion (Anführungszeichen um EOF):

cat << 'EOF'
Hier wird $USER NICHT ersetzt.
Alles bleibt literal.
EOF

Methode 2: Anführungszeichen:

text="Zeile 1
Zeile 2
Zeile 3"

echo "$text"

Methode 3: String-Verkettung:

text="Zeile 1\n"
text+="Zeile 2\n"
text+="Zeile 3"

echo -e "$text"  # -e interpretiert \n als Zeilenumbruch

5.2 String-Interpolation

Einfache Anführungszeichen vs. doppelte Anführungszeichen:

name="Max"

# Doppelte Anführungszeichen: Variablen werden ersetzt
echo "Hallo $name"    # Ausgabe: Hallo Max

# Einfache Anführungszeichen: Alles literal
echo 'Hallo $name'    # Ausgabe: Hallo $name

Geschweifte Klammern für Eindeutigkeit:

frucht="Apfel"

echo "$fruchtmus"     # Fehler: Variable $fruchtmus existiert nicht
echo "${frucht}mus"   # Korrekt: Apfelmus

Befehlssubstitution im String:

echo "Heute ist $(date +%A)"           # Ausgabe: Heute ist Montag
echo "Es gibt $(ls | wc -l) Dateien"   # Ausgabe: Es gibt 42 Dateien

Arithmetik im String:

echo "5 + 3 = $((5 + 3))"  # Ausgabe: 5 + 3 = 8

String-Manipulationen in zsh:

text="Hallo Welt"

# Länge
echo ${#text}              # Ausgabe: 10

# Substring (0-basiert)
echo ${text:0:5}           # Ausgabe: Hallo
echo ${text:6}             # Ausgabe: Welt

# Ersetzen
echo ${text/Welt/Max}      # Ausgabe: Hallo Max
echo ${text//l/L}          # Alle ersetzen: HaLLo WeLt

# Groß-/Kleinschreibung (zsh)
echo ${text:u}             # Ausgabe: HALLO WELT
echo ${text:l}             # Ausgabe: hallo welt

6 Argumente verarbeiten

Skripte und Funktionen können Argumente entgegennehmen, die beim Aufruf übergeben werden.

6.1 Zugriff über $1, $2

Die übergebenen Argumente sind über nummerierte Variablen zugänglich:

VariableBedeutung
$0Name des Skripts
$1Erstes Argument
$2Zweites Argument
$3$9Weitere Argumente
${10}Ab 10: Geschweifte Klammern nötig
$#Anzahl der Argumente

Beispiel:

#!/bin/zsh
# Datei: greet.sh

echo "Skriptname: $0"
echo "Erstes Argument: $1"
echo "Zweites Argument: $2"
echo "Anzahl Argumente: $#"

Aufruf:

./greet.sh Hallo Welt
# Ausgabe:
# Skriptname: ./greet.sh
# Erstes Argument: Hallo
# Zweites Argument: Welt
# Anzahl Argumente: 2

Prüfen ob Argument vorhanden:

#!/bin/zsh

if [-z "$1"](-z%20"$1".md); then
    echo "Fehler: Kein Argument angegeben"
    echo "Verwendung: $0 <name>"
    exit 1
fi

echo "Hallo $1!"

6.2 $@ und $*

Beide repräsentieren alle übergebenen Argumente, verhalten sich aber unterschiedlich:

$@ – Jedes Argument separat (empfohlen):

#!/bin/zsh
# Datei: args.sh

echo "Mit \$@:"
for arg in "$@"; do
    echo "  Argument: '$arg'"
done

Aufruf:

./args.sh "Hallo Welt" foo bar
# Ausgabe:
#   Argument: 'Hallo Welt'
#   Argument: 'foo'
#   Argument: 'bar'

$* – Alle Argumente als ein String:

#!/bin/zsh

echo "Mit \$*:"
for arg in "$*"; do
    echo "  Argument: '$arg'"
done

Aufruf:

./args.sh "Hallo Welt" foo bar
# Ausgabe:
#   Argument: 'Hallo Welt foo bar'

Zusammenfassung:

SyntaxBedeutung
"$@"Jedes Argument als separates Wort (Leerzeichen in Argumenten bleiben erhalten)
"$*"Alle Argumente als ein einziger String
$@ / $*Ohne Anführungszeichen: Wörter werden bei Leerzeichen getrennt

In der Regel wird "$@" verwendet.

Alle Argumente an anderen Befehl weitergeben:

#!/bin/zsh
# Wrapper-Skript

echo "Starte Programm mit allen Argumenten..."
/pfad/zum/programm "$@"

6.3 Optionen parsen (getopts)

Für Skripte mit Optionen (z. B. -v, -f datei) bietet getopts eine strukturierte Lösung.

Grundsyntax:

while getopts "vf:o:" opt; do
    case $opt in
        v) verbose=true ;;
        f) input_file="$OPTARG" ;;
        o) output_file="$OPTARG" ;;
        ?) echo "Ungültige Option"; exit 1 ;;
    esac
done

Erklärung:

  • v – Option ohne Wert (Flag)
  • f: – Option mit erforderlichem Wert (Doppelpunkt)
  • $OPTARG – Enthält den Wert der Option

Vollständiges Beispiel:

#!/bin/zsh
# Datei: prozess.sh

# Standardwerte
verbose=false
input_file=""
output_file="output.txt"

# Hilfe-Funktion
show_help() {
    echo "Verwendung: $0 [-v] [-f eingabe] [-o ausgabe]"
    echo ""
    echo "Optionen:"
    echo "  -v          Ausführliche Ausgabe"
    echo "  -f DATEI    Eingabedatei (erforderlich)"
    echo "  -o DATEI    Ausgabedatei (Standard: output.txt)"
    echo "  -h          Diese Hilfe anzeigen"
}

# Optionen parsen
while getopts "vf:o:h" opt; do
    case $opt in
        v) verbose=true ;;
        f) input_file="$OPTARG" ;;
        o) output_file="$OPTARG" ;;
        h) show_help; exit 0 ;;
        ?) show_help; exit 1 ;;
    esac
done

# Restliche Argumente (nach den Optionen)
shift $((OPTIND - 1))
remaining_args="$@"

# Validierung
if [-z "$input_file"](-z%20"$input_file".md); then
    echo "Fehler: Eingabedatei erforderlich (-f)"
    exit 1
fi

# Hauptlogik
if $verbose; then
    echo "Eingabe: $input_file"
    echo "Ausgabe: $output_file"
    echo "Weitere Argumente: $remaining_args"
fi

echo "Verarbeite $input_file..."

Aufruf:

./prozess.sh -v -f input.txt -o result.txt zusatz1 zusatz2

7 Rückgabewerte und Exit-Codes

Jeder Befehl in Unix gibt einen Exit-Code zurück, der Erfolg oder Misserfolg signalisiert.

7.1 exit und $?

Exit-Codes:

CodeBedeutung
0Erfolg
1Allgemeiner Fehler
2Falsche Verwendung (z. B. fehlende Argumente)
126Befehl nicht ausführbar
127Befehl nicht gefunden
128+NBeendet durch Signal N
130Beendet durch Ctrl+C (SIGINT)

$? – Exit-Code des letzten Befehls:

ls /existiert
echo $?  # Ausgabe: 0 (Erfolg)

ls /existiert_nicht
echo $?  # Ausgabe: 1 (Fehler)

exit in Skripten:

#!/bin/zsh

if [! -f "$1"](!%20-f%20"$1".md); then
    echo "Fehler: Datei nicht gefunden"
    exit 1
fi

echo "Datei gefunden"
exit 0

return in Funktionen:

Funktionen verwenden return statt exit:

pruefe_datei() {
    if [-f "$1"](-f%20"$1".md); then
        return 0  # Erfolg
    else
        return 1  # Fehler
    fi
}

if pruefe_datei "test.txt"; then
    echo "Datei existiert"
else
    echo "Datei nicht gefunden"
fi

Exit-Code direkt in Bedingung:

if grep -q "suchbegriff" datei.txt; then
    echo "Gefunden"
else
    echo "Nicht gefunden"
fi

7.2 Fehlerbehandlung

Befehlsverkettung mit && und ||:

# && – Nächster Befehl nur bei Erfolg
mkdir neuer_ordner && cd neuer_ordner && echo "Erfolgreich"

# || – Nächster Befehl nur bei Fehler
cd /existiert_nicht || echo "Verzeichnis nicht gefunden"

# Kombination
cd /verzeichnis && echo "OK" || echo "Fehler"

Prüfung mit if:

#!/bin/zsh

if ! command -v git &> /dev/null; then
    echo "Git ist nicht installiert"
    exit 1
fi

echo "Git ist verfügbar"

Fehlerausgabe umleiten:

# Nur Fehler unterdrücken
befehl 2>/dev/null

# Alles unterdrücken
befehl &>/dev/null

# Fehler in Datei schreiben
befehl 2>> fehler.log

8 Ausführen und Debuggen

8.1 chmod +x

Bevor ein Skript direkt ausgeführt werden kann, muss es als ausführbar markiert werden:

# Ausführbar machen
chmod +x mein_skript.sh

# Ausführen
./mein_skript.sh

Alternativ ohne chmod:

zsh mein_skript.sh
bash mein_skript.sh

Berechtigungen prüfen:

ls -l mein_skript.sh
# -rwxr-xr-x  1 user  staff  123 Jan 1 12:00 mein_skript.sh
#  ^^^
#  Die x's zeigen Ausführbarkeit an

8.2 set -x Debugging

Mit set -x wird jeder ausgeführte Befehl vor der Ausführung angezeigt:

#!/bin/zsh
set -x  # Debug-Modus aktivieren

name="Max"
echo "Hallo $name"
datum=$(date)
echo "Datum: $datum"

Ausgabe:

+name=Max
+echo 'Hallo Max'
Hallo Max
+date
+datum='Mon Jan 1 12:00:00 CET 2024'
+echo 'Datum: Mon Jan 1 12:00:00 CET 2024'
Datum: Mon Jan 1 12:00:00 CET 2024

Debug-Modus ein-/ausschalten:

set -x  # Aktivieren
# ... Debug-relevanter Code ...
set +x  # Deaktivieren

Nur beim Aufruf aktivieren:

zsh -x mein_skript.sh
bash -x mein_skript.sh

8.3 Fehlerbehandlung (set -e, trap)

set -e – Bei Fehler abbrechen:

Standardmäßig läuft ein Skript weiter, auch wenn ein Befehl fehlschlägt. Mit set -e wird bei jedem Fehler abgebrochen:

#!/bin/zsh
set -e

echo "Schritt 1"
falsch_geschriebener_befehl  # Skript bricht hier ab
echo "Schritt 2"             # Wird nicht erreicht

set -u – Fehler bei undefinierten Variablen:

#!/bin/zsh
set -u

echo $undefinierte_variable  # Skript bricht ab

set -o pipefail – Pipe-Fehler erkennen:

#!/bin/zsh
set -o pipefail

# Ohne pipefail: Exit-Code ist 0 (von wc)
# Mit pipefail: Exit-Code ist 1 (von cat)
cat nicht_vorhanden.txt | wc -l

Empfohlene Kombination:

#!/bin/zsh
set -euo pipefail

trap – Aufräumen bei Beendigung:

trap führt Befehle aus, wenn das Skript beendet wird (normal oder durch Fehler):

#!/bin/zsh
set -e

# Temporäre Datei erstellen
temp_file=$(mktemp)
echo "Temp-Datei: $temp_file"

# Bei Beendigung aufräumen
trap "rm -f $temp_file; echo 'Aufgeräumt'" EXIT

# Hauptlogik
echo "Arbeite..."
sleep 2
echo "Fertig"

# temp_file wird automatisch gelöscht

Verschiedene Signale abfangen:

#!/bin/zsh

cleanup() {
    echo "Räume auf..."
    # Aufräumarbeiten hier
}

trap cleanup EXIT      # Bei normalem Ende
trap cleanup SIGINT    # Bei Ctrl+C
trap cleanup SIGTERM   # Bei kill

Praktisches Beispiel:

#!/bin/zsh
set -euo pipefail

# Temporäres Verzeichnis
work_dir=$(mktemp -d)
trap "rm -rf $work_dir" EXIT

echo "Arbeitsverzeichnis: $work_dir"

# Dateien im temp-Verzeichnis verarbeiten
cd "$work_dir"
curl -O https://example.com/datei.zip
unzip datei.zip
# ... weitere Verarbeitung ...

# work_dir wird automatisch gelöscht

8.4 Profiling (time, dtruss)

time – Ausführungszeit messen:

time ./mein_skript.sh

Ausgabe:

./mein_skript.sh  0.05s user 0.02s system 89% cpu 0.078 total
WertBedeutung
userCPU-Zeit im User-Mode
systemCPU-Zeit im Kernel-Mode
cpuCPU-Auslastung in Prozent
totalGesamte verstrichene Zeit

Einzelne Befehle messen:

time sleep 2
time find / -name "*.log" 2>/dev/null

dtruss – Systemaufrufe verfolgen (macOS):

dtruss ist das macOS-Äquivalent zu strace unter Linux. Es zeigt alle Systemaufrufe eines Prozesses:

# Erfordert Root-Rechte
sudo dtruss ./mein_skript.sh

Nur bestimmte Syscalls:

sudo dtruss -t open ./mein_skript.sh   # Nur open()-Aufrufe
sudo dtruss -t read ./mein_skript.sh   # Nur read()-Aufrufe

Laufenden Prozess analysieren:

sudo dtruss -p <PID>

Alternative: dtrace:

Für komplexere Analysen bietet macOS dtrace:

sudo dtrace -n 'syscall:::entry /execname == "zsh"/ { @[probefunc] = count(); }'

8.5 Shebang #!/bin/zsh

Die erste Zeile eines Skripts (Shebang) bestimmt, welcher Interpreter verwendet wird:

#!/bin/zsh

Häufige Shebangs:

ShebangInterpreter
#!/bin/zshZ-Shell (macOS-Standard)
#!/bin/bashBash
#!/bin/shPOSIX-Shell (portabel)
#!/usr/bin/env zshzsh aus PATH (flexibler)
#!/usr/bin/env python3Python 3

/usr/bin/env – Flexibler Ansatz:

#!/usr/bin/env zsh

Vorteile:

  • Findet den Interpreter im $PATH
  • Funktioniert auch wenn zsh nicht in /bin/zsh liegt
  • Portabler zwischen verschiedenen Systemen

Wichtig:

  • Der Shebang muss in der ersten Zeile stehen
  • Vor #! dürfen keine Zeichen stehen (auch keine Leerzeichen)
  • Die Zeile muss mit einem Zeilenumbruch enden

9 Formatierung der Ausgabe

Insbesondere beim Schreiben von Aliasen, Funktionen und Skripten kann der Einsatz Farbe und Fettmarkierungen hilfreich sein, um die Ausgabe übersichtlich zu halten.

Beispiele:

echo "\033[31mRot\033[0m"          # Rot
echo "\033[32mGrün\033[0m"         # Grün
echo "\033[1;33mFett Gelb\033[0m"  # Fetter Text
echo -e "\033[34m$(pwd)\033[0m"    # Ausgabe von `pwd` in blau
  • \033[ leitet eine Escape-Sequenz ein
  • 31 = Rot, 32 = Grün, 34 = Blau
  • 1 = Fett
  • 0m = Zurücksetzen der Formatierung

Häufig verwendete Farben:

CodeFarbeStil
30Schwarznormal
31Rotnormal
32Grünnormal
33Gelbnormal
34Blaunormal
35Magentanormal
36Cyannormal
37Weißnormal

Stile:

Erweiterte Stiltabelle:

CodeStil
0Reset/Normal
1Fett
2Gedimmt
3Kursiv
4Unterstrichen
5Blinkend
7Invertiert
9Durchgestrichen

warning

Kursiv wird nicht von allen Terminals unterstützt. In manchen Terminals wird es als inverse Darstellung oder gar nicht angezeigt. Die meisten modernen Terminal-Emulatoren (iTerm2, Terminal.app ab neueren Versionen, etc.) unterstützen es jedoch.

10 Skript-Vorlage

#!/usr/bin/env zsh
set -euo pipefail

# [Short description of the script]

# --- Configuration ---
readonly SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
readonly SCRIPT_NAME="$(basename "$0")"

# --- Functions ---
show_help() {
    cat << EOF
Usage: $SCRIPT_NAME [OPTIONS] <argument>

Description of the script.

Options:
  -h, --help    Show this help

Example:
  $SCRIPT_NAME -v input.txt
EOF
}

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" }


# --- Main program ---
main() {
    if [$]($.md#-eq-0); then
        show_help
        exit 1
    fi

    # Parse command line options
    while getopts "h:" opt; do  # Add further flags here
        case $opt in
            h)  show_help; exit 0 ;;
            \?) echo "Invalid option: -$OPTARG" >&2; exit 1 ;;
            :)  echo "Option -$OPTARG requires an argument" >&2; exit 1 ;;
        esac
    done

    log "Script started"
    # ... Main logic here ...
    log "Script finished"
}

main "$@"

Regex, sed & awk

Reguläre Ausdrücke (Regex) sind ein mächtiges Werkzeug zur Mustererkennung in Texten. In Kombination mit sed und awk ermöglichen sie komplexe Textverarbeitung direkt im Terminal.

1 Regex-Grundlagen

Reguläre Ausdrücke beschreiben Suchmuster für Text. Sie werden von vielen Tools unterstützt: grep, sed, awk, find, vim und Programmiersprachen wie Python, JavaScript und Rust.

1.1 Literale Zeichen

Die einfachste Form: Zeichen matchen sich selbst.

echo "Hallo Welt" | grep "Welt"   # Findet "Welt"
echo "Hallo Welt" | grep "xyz"    # Keine Ausgabe

1.2 Metazeichen

Metazeichen haben eine besondere Bedeutung:

ZeichenBedeutungBeispielMatcht
.Beliebiges einzelnes ZeichenH.lloHallo, Hello, H2llo
^Zeilenanfang^Hallo“Hallo” am Anfang
$ZeilenendeWelt$“Welt” am Ende
*0 oder mehr des vorherigenab*cac, abc, abbc, abbbc
+1 oder mehr des vorherigenab+cabc, abbc (nicht ac)
?0 oder 1 des vorherigencolou?rcolor, colour
\Escapen von Metazeichen\.Literaler Punkt

Beispiele:

# Zeilen die mit "Error" beginnen
grep "^Error" logfile.txt

# Zeilen die mit ".txt" enden
grep "\.txt$" dateiliste.txt

# Beliebiges Zeichen zwischen H und llo
echo -e "Hallo\nHello\nHullo" | grep "H.llo"

1.3 Zeichenklassen

Zeichenklassen matchen eines von mehreren Zeichen:

SyntaxBedeutungBeispiel
[abc]a, b oder c[Hh]allo matcht “Hallo” und “hallo”
[^abc]Nicht a, b oder c[^0-9] matcht Nicht-Ziffern
[a-z]Kleinbuchstaben a–z[a-zA-Z] alle Buchstaben
[0-9]Ziffern 0–9[0-9]+ eine oder mehr Ziffern

Vordefinierte Zeichenklassen (POSIX):

KlasseBedeutungÄquivalent
[:alpha:](:alpha:.md)Buchstaben[a-zA-Z]
[:digit:](:digit:.md)Ziffern[0-9]
[:alnum:](:alnum:.md)Buchstaben und Ziffern[a-zA-Z0-9]
[:space:](:space:.md)Whitespace[ \t\n\r]
[:upper:](:upper:.md)Großbuchstaben[A-Z]
[:lower:](:lower:.md)Kleinbuchstaben[a-z]
[:punct:](:punct:.md)Satzzeichen[.,!?;:] etc.

Beispiele:

# Nur Zeilen mit Ziffern
grep "[0-9]" datei.txt

# Zeilen die mit Großbuchstaben beginnen
grep "^[A-Z]" datei.txt

# Wörter mit Bindestrich
grep "[a-z]-[a-z]" datei.txt

1.4 Quantifizierer

Quantifizierer bestimmen, wie oft ein Element vorkommen darf:

SyntaxBedeutungBeispiel
*0 oder mehra* matcht “”, a, aa, aaa
+1 oder mehra+ matcht a, aa, aaa (nicht “”)
?0 oder 1a? matcht “”, a
{n}Genau n-mala{3} matcht aaa
{n,}Mindestens n-mala{2,} matcht aa, aaa, aaaa
{n,m}n bis m-mala{2,4} matcht aa, aaa, aaaa

Beispiele:

# Telefonnummern (vereinfacht)
grep -E "[0-9]{3,5}" kontakte.txt

# Postleitzahlen (5 Ziffern)
grep -E "^[0-9]{5}$" plz.txt

# Optionaler Bindestrich
grep -E "[0-9]{4}-?[0-9]{4}" nummern.txt

1.5 Gruppen und Alternativen

SyntaxBedeutungBeispiel
(...)Gruppierung(ab)+ matcht ab, abab, ababab
|Alternative (ODER)cat|dog matcht cat oder dog
\1, \2Rückreferenz(.)\1 matcht aa, bb, cc (Dopplungen)

Beispiele:

# "error" oder "warning" (case-insensitive)
grep -iE "(error|warning)" logfile.txt

# Wiederholte Wörter finden
grep -E "\b(\w+)\s+\1\b" text.txt

# Entweder http oder https
grep -E "https?://" urls.txt

1.6 Anker und Wortgrenzen

SyntaxBedeutung
^Zeilenanfang
$Zeilenende
\bWortgrenze
\BKeine Wortgrenze
\<Wortanfang
\>Wortende

Beispiele:

# Nur das Wort "the" (nicht "there", "other")
grep -E "\bthe\b" text.txt

# Zeilen die nur aus Ziffern bestehen
grep -E "^[0-9]+$" datei.txt

# Leere Zeilen
grep -E "^$" datei.txt

1.7 BRE vs. ERE

Es gibt zwei Regex-Varianten:

BRE (Basic Regular Expressions):

  • Standard bei grep und sed
  • +, ?, |, () müssen escaped werden: \+, \?, \|, \(\)

ERE (Extended Regular Expressions):

  • Bei grep -E (oder egrep) und sed -E
  • +, ?, |, () funktionieren direkt
# BRE: Escaping nötig
grep "https\?" urls.txt
sed 's/\(error\|warning\)/PROBLEM/g' log.txt

# ERE: Kein Escaping
grep -E "https?" urls.txt
sed -E 's/(error|warning)/PROBLEM/g' log.txt

Empfehlung: ERE verwenden (grep -E, sed -E) für bessere Lesbarkeit.

1.8 Regex-Schnellreferenz

Zeichen:
  .         Beliebiges Zeichen
  \d        Ziffer (manche Tools)
  \w        Wortzeichen [a-zA-Z0-9_]
  \s        Whitespace

Anker:
  ^         Zeilenanfang
  $         Zeilenende
  \b        Wortgrenze

Quantifizierer:
  *         0 oder mehr
  +         1 oder mehr
  ?         0 oder 1
  {n}       Genau n
  {n,m}     n bis m

Klassen:
  [abc]     a, b oder c
  [^abc]    Nicht a, b, c
  [a-z]     Bereich a bis z

Gruppen:
  (...)     Gruppe
  |         Alternative
  \1        Rückreferenz

2 sed – Stream Editor

sed (Stream Editor) verarbeitet Text zeilenweise und führt Transformationen durch. Es ist ideal für automatisierte Ersetzungen und Textmanipulationen.

2.1 Grundsyntax

sed [OPTIONEN] 'BEFEHL' [DATEI]

Häufige Optionen:

OptionBedeutung
-iIn-place: Datei direkt ändern
-i ''In-place ohne Backup (macOS)
-i.bakIn-place mit Backup
-EExtended Regex (ERE)
-nKeine automatische Ausgabe

2.2 Suchen und Ersetzen (s)

Das häufigste sed-Kommando:

sed 's/SUCHE/ERSETZUNG/FLAGS'

Flags:

FlagBedeutung
gGlobal: Alle Vorkommen ersetzen
iCase-insensitive
pZeile ausgeben (mit -n)
1, 2, …Nur n-tes Vorkommen ersetzen

Beispiele:

# Erstes Vorkommen pro Zeile
echo "hallo hallo" | sed 's/hallo/hi/'
# Ausgabe: hi hallo

# Alle Vorkommen
echo "hallo hallo" | sed 's/hallo/hi/g'
# Ausgabe: hi hi

# Case-insensitive
echo "Hallo HALLO" | sed 's/hallo/hi/gi'
# Ausgabe: hi hi

# Nur zweites Vorkommen
echo "a a a a" | sed 's/a/X/2'
# Ausgabe: a X a a

2.3 Trennzeichen

Das Trennzeichen muss nicht / sein – nützlich bei Pfaden:

# Mit /
sed 's/\/usr\/local/\/opt/g'

# Besser: Anderes Trennzeichen
sed 's|/usr/local|/opt|g'
sed 's#/usr/local#/opt#g'
sed 's@/usr/local@/opt@g'

2.4 Gruppen und Rückreferenzen

Mit \(...\) (BRE) oder (...) (ERE) können Teile erfasst und mit \1, \2 wiederverwendet werden:

# Reihenfolge tauschen
echo "Max Mustermann" | sed -E 's/([^ ]+) ([^ ]+)/\2, \1/'
# Ausgabe: Mustermann, Max

# Datum umformatieren
echo "2024-01-15" | sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3.\2.\1/'
# Ausgabe: 15.01.2024

# Wörter verdoppeln
echo "test" | sed -E 's/(.+)/\1 \1/'
# Ausgabe: test test

# HTML-Tags entfernen
echo "<b>Text</b>" | sed -E 's/<[^>]+>//g'
# Ausgabe: Text

2.5 Adressierung – Zeilen auswählen

Befehle können auf bestimmte Zeilen beschränkt werden:

AdresseBedeutung
5Nur Zeile 5
5,10Zeilen 5 bis 10
$Letzte Zeile
/muster/Zeilen die Muster enthalten
1,/muster/Von Zeile 1 bis erste Zeile mit Muster

Beispiele:

# Nur Zeile 3 bearbeiten
sed '3s/alt/neu/' datei.txt

# Zeilen 2-5 bearbeiten
sed '2,5s/alt/neu/' datei.txt

# Letzte Zeile bearbeiten
sed '$s/alt/neu/' datei.txt

# Nur Zeilen mit "error"
sed '/error/s/status/STATUS/' log.txt

# Ab "START" bis "END"
sed '/START/,/END/s/foo/bar/g' datei.txt

2.6 Weitere sed-Befehle

BefehlBedeutung
dZeile löschen
pZeile ausgeben
a\TEXTText nach Zeile einfügen
i\TEXTText vor Zeile einfügen
c\TEXTZeile ersetzen
y/abc/xyz/Zeichen transliterieren

Beispiele:

# Zeilen löschen
sed '3d' datei.txt              # Zeile 3 löschen
sed '/^#/d' config.txt          # Kommentare löschen
sed '/^$/d' datei.txt           # Leere Zeilen löschen

# Zeilen ausgeben (mit -n)
sed -n '5p' datei.txt           # Nur Zeile 5 ausgeben
sed -n '10,20p' datei.txt       # Zeilen 10-20
sed -n '/error/p' log.txt       # Nur Zeilen mit "error"

# Text einfügen
sed '3a\Neue Zeile' datei.txt   # Nach Zeile 3
sed '1i\Kopfzeile' datei.txt    # Vor Zeile 1

# Zeichen ersetzen (wie tr)
echo "hello" | sed 'y/aeiou/AEIOU/'
# Ausgabe: hEllO

2.7 Mehrere Befehle

# Mit -e
sed -e 's/foo/bar/' -e 's/baz/qux/' datei.txt

# Mit Semikolon
sed 's/foo/bar/; s/baz/qux/' datei.txt

# Mehrzeilig
sed '
  s/foo/bar/g
  s/baz/qux/g
  /^#/d
' datei.txt

2.8 In-place-Bearbeitung

# macOS: Leeres Backup-Suffix erforderlich
sed -i '' 's/alt/neu/g' datei.txt

# Mit Backup
sed -i '.bak' 's/alt/neu/g' datei.txt

# Linux: Kein Suffix nötig
sed -i 's/alt/neu/g' datei.txt

2.9 Praktische sed-Beispiele

# Leerzeichen am Zeilenende entfernen
sed 's/[:space:](:space:.md)*$//' datei.txt

# Mehrere Leerzeichen zu einem
sed -E 's/ +/ /g' datei.txt

# Erste Zeile jeder Datei (wie head -1)
sed -n '1p' *.txt

# Zeilen 5-10 löschen
sed '5,10d' datei.txt

# Jede Zeile nummerieren
sed = datei.txt | sed 'N; s/\n/\t/'

# Zwischen zwei Markern ersetzen
sed '/BEGIN/,/END/s/old/new/g' datei.txt

# Konfigurationswert ändern
sed -E 's/^(port=).*/\18080/' config.txt

# CSV: Spaltenreihenfolge ändern
sed -E 's/([^,]+),([^,]+),([^,]+)/\3,\1,\2/' data.csv

3 awk – Textanalyse und Berichte

awk ist eine vollständige Programmiersprache für Textverarbeitung. Es eignet sich besonders für spaltenbasierte Daten wie CSV, Logs und tabellarische Ausgaben.

3.1 Grundsyntax

awk [OPTIONEN] 'PROGRAMM' [DATEI]

Ein awk-Programm besteht aus Regeln:

MUSTER { AKTION }
  • Zeilen die dem MUSTER entsprechen, führen die AKTION aus
  • Ohne MUSTER: Aktion auf alle Zeilen
  • Ohne AKTION: Zeile ausgeben

3.2 Felder und Variablen

awk teilt jede Zeile automatisch in Felder:

VariableBedeutung
$0Gesamte Zeile
$1Erstes Feld
$2Zweites Feld
$NFLetztes Feld
$(NF-1)Vorletztes Feld
NFAnzahl der Felder
NRAktuelle Zeilennummer
FNRZeilennummer in aktueller Datei
FSFeldtrenner (Standard: Whitespace)
OFSAusgabe-Feldtrenner
RSZeilentrenner
ORSAusgabe-Zeilentrenner

Beispiele:

# Erste Spalte ausgeben
echo "Max 30 Berlin" | awk '{print $1}'
# Ausgabe: Max

# Mehrere Spalten
echo "Max 30 Berlin" | awk '{print $1, $3}'
# Ausgabe: Max Berlin

# Spalten in anderer Reihenfolge
echo "Max 30 Berlin" | awk '{print $3, $1, $2}'
# Ausgabe: Berlin Max 30

# Letzte Spalte
echo "a b c d e" | awk '{print $NF}'
# Ausgabe: e

# Anzahl Felder
echo "a b c d e" | awk '{print NF}'
# Ausgabe: 5

3.3 Feldtrenner ändern

# Mit -F
awk -F',' '{print $1}' datei.csv
awk -F':' '{print $1, $3}' /etc/passwd

# Im Programm
awk 'BEGIN{FS=","} {print $1}' datei.csv

# Mehrere Trennzeichen
awk -F'[,;:]' '{print $1}' datei.txt

# Tab als Trenner
awk -F'\t' '{print $2}' datei.tsv

3.4 Muster (Pattern)

# Zeilen mit "error"
awk '/error/' log.txt

# Zeilen die NICHT "comment" enthalten
awk '!/comment/' datei.txt

# Zeilen wo Feld 3 größer als 100
awk '$3 > 100' daten.txt

# Zeilen wo Feld 1 einem Muster entspricht
awk '$1 ~ /^A/' namen.txt

# Kombination
awk '/error/ && $3 > 5' log.txt

# Bereichsmuster
awk '/START/,/END/' datei.txt

3.5 BEGIN und END

BEGIN wird vor der ersten Zeile ausgeführt, END nach der letzten:

# Kopfzeile ausgeben
awk 'BEGIN{print "Name\tAlter"} {print $1, $2}' daten.txt

# Summe berechnen
awk '{sum += $1} END{print "Summe:", sum}' zahlen.txt

# Zeilen zählen
awk 'END{print NR, "Zeilen"}' datei.txt

# Durchschnitt berechnen
awk '{sum += $1; count++} END{print "Durchschnitt:", sum/count}' zahlen.txt

3.6 Formatierte Ausgabe mit printf

# Grundsyntax
awk '{printf "%-10s %5d\n", $1, $2}' daten.txt

Format-Spezifizierer:

FormatBedeutung
%sString
%dInteger
%fFließkommazahl
%.2f2 Dezimalstellen
%10s10 Zeichen breit, rechtsbündig
%-10s10 Zeichen breit, linksbündig

Beispiele:

# Tabelle formatieren
awk '{printf "%-15s %8.2f\n", $1, $2}' preise.txt

# Mit Kopfzeile
awk 'BEGIN{printf "%-15s %8s\n", "Produkt", "Preis"; print "------------------------"}
     {printf "%-15s %8.2f\n", $1, $2}' preise.txt

3.7 Variablen und Berechnungen

# Eigene Variablen
awk '{total = $2 * $3; print $1, total}' bestellung.txt

# Externe Variablen übergeben
threshold=100
awk -v limit="$threshold" '$2 > limit {print}' daten.txt

# Mehrere Variablen
awk -v min=10 -v max=50 '$1 >= min && $1 <= max' zahlen.txt

3.8 Kontrollstrukturen

if-else:

awk '{
    if ($3 > 100)
        print $1, "hoch"
    else if ($3 > 50)
        print $1, "mittel"
    else
        print $1, "niedrig"
}' daten.txt

for-Schleife:

# Alle Felder ausgeben
awk '{for(i=1; i<=NF; i++) print i, $i}' datei.txt

# Felder rückwärts
awk '{for(i=NF; i>=1; i--) printf "%s ", $i; print ""}' datei.txt

while-Schleife:

awk '{
    i = 1
    while(i <= NF) {
        print $i
        i++
    }
}' datei.txt

3.9 Arrays

# Werte zählen
awk '{count[$1]++} END{for(key in count) print key, count[key]}' log.txt

# Summen nach Kategorie
awk '{sum[$1] += $2} END{for(cat in sum) print cat, sum[cat]}' verkauf.txt

# Duplikate finden
awk 'seen[$0]++' datei.txt

# Einzigartige Zeilen (wie uniq)
awk '!seen[$0]++' datei.txt

3.10 Eingebaute Funktionen

String-Funktionen:

FunktionBedeutung
length(s)Länge des Strings
substr(s,i,n)Substring ab Position i, n Zeichen
index(s,t)Position von t in s
split(s,a,fs)String in Array aufteilen
sub(r,s)Erstes Vorkommen ersetzen
gsub(r,s)Alle Vorkommen ersetzen
tolower(s)In Kleinbuchstaben
toupper(s)In Großbuchstaben

Beispiele:

# Stringlänge
echo "Hallo Welt" | awk '{print length($0)}'
# Ausgabe: 10

# Substring
echo "Hallo Welt" | awk '{print substr($0, 1, 5)}'
# Ausgabe: Hallo

# Ersetzen
echo "foo bar foo" | awk '{gsub(/foo/, "baz"); print}'
# Ausgabe: baz bar baz

# Großbuchstaben
echo "hallo" | awk '{print toupper($0)}'
# Ausgabe: HALLO

Mathematische Funktionen:

FunktionBedeutung
int(x)Ganzzahliger Anteil
sqrt(x)Quadratwurzel
sin(x), cos(x)Trigonometrische Funktionen
log(x)Natürlicher Logarithmus
exp(x)e^x
rand()Zufallszahl 0–1

3.11 Praktische awk-Beispiele

# CSV: Bestimmte Spalten extrahieren
awk -F',' '{print $1, $3}' data.csv

# Summe einer Spalte
awk '{sum += $2} END{print sum}' zahlen.txt

# Durchschnitt
awk '{sum += $1; n++} END{print sum/n}' zahlen.txt

# Maximum finden
awk 'BEGIN{max=0} $1>max{max=$1} END{print max}' zahlen.txt

# Zeilen mit mehr als 3 Feldern
awk 'NF > 3' datei.txt

# Jede zweite Zeile
awk 'NR % 2 == 0' datei.txt

# Zeilen zwischen zwei Markern (ohne Marker)
awk '/START/{flag=1; next} /END/{flag=0} flag' datei.txt

# IP-Adressen extrahieren aus Log
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head

# Dateigröße pro Verzeichnis
ls -l | awk '/^-/{sum += $5} END{print "Total:", sum, "Bytes"}'

# Spalten vertauschen
awk '{print $2, $1, $3}' datei.txt

# Bestimmte Zeilen und Spalten
awk 'NR >= 5 && NR <= 10 {print $1, $3}' datei.txt

# Feldtrenner für Ausgabe setzen
awk 'BEGIN{OFS=","} {print $1, $2, $3}' datei.txt

# Top 10 häufigste Werte
awk '{print $1}' datei.txt | sort | uniq -c | sort -rn | head -10

4 Kombinationsbeispiele (Pipe-Workflows)

Die wahre Stärke von sed, awk und Regex zeigt sich in Kombination mit anderen Unix-Tools.

4.1 Log-Analyse

Fehler aus Log extrahieren und zählen:

grep -i "error" application.log | \
    awk '{print $4}' | \
    sort | uniq -c | sort -rn | head -10

Zugriffe pro Stunde aus Access-Log:

awk '{print $4}' access.log | \
    cut -d: -f2 | \
    sort | uniq -c | \
    awk '{print $2":00", $1}'

IP-Adressen mit den meisten Fehlern:

grep " 404 \| 500 " access.log | \
    awk '{print $1}' | \
    sort | uniq -c | sort -rn | head -10

Langsame Requests finden (> 5 Sekunden):

awk '$NF > 5000 {print $7, $NF"ms"}' access.log | \
    sort -t' ' -k2 -rn | head -20

4.2 Dateitransformationen

CSV zu TSV konvertieren:

sed 's/,/\t/g' data.csv > data.tsv

JSON-Lines zu CSV (einfach):

cat data.jsonl | \
    sed 's/[{}":]//g' | \
    sed 's/,/\t/g'

Konfigurationsdatei bereinigen:

# Kommentare und leere Zeilen entfernen, sortieren
grep -v '^#' config.txt | \
    grep -v '^$' | \
    sed 's/[:space:](:space:.md)*$//' | \
    sort

Markdown-Überschriften extrahieren:

grep -E "^#{1,3} " document.md | \
    sed 's/^#* //' | \
    awk '{printf "%d. %s\n", NR, $0}'

4.3 System-Administration

Disk-Usage sortiert nach Größe:

du -sh * 2>/dev/null | \
    sort -rh | head -20

Größte Dateien finden:

find . -type f -exec ls -la {} \; 2>/dev/null | \
    awk '{print $5, $9}' | \
    sort -rn | head -20

Prozesse nach Memory-Verbrauch:

ps aux | \
    awk 'NR>1 {print $4, $11}' | \
    sort -rn | head -10

Offene Ports auflisten:

netstat -an | \
    grep LISTEN | \
    awk '{print $4}' | \
    sed 's/.*\.//' | \
    sort -n | uniq

4.4 Textbereinigung

Mehrfache Leerzeichen und -zeilen bereinigen:

cat messy.txt | \
    sed 's/[:space:](:space:.md)\+/ /g' | \
    sed 's/^[:space:](:space:.md)//' | \
    sed 's/[:space:](:space:.md)$//' | \
    cat -s

HTML-Tags entfernen:

sed -E 's/<[^>]+>//g' page.html | \
    sed '/^$/d' | \
    sed 's/&nbsp;/ /g; s/&amp;/\&/g; s/&lt;/</g; s/&gt;/>/g'

E-Mail-Adressen extrahieren:

grep -oE '[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}' dokument.txt | \
    sort -u

URLs extrahieren:

grep -oE 'https?://[^[:space:]"<>]+' webpage.html | \
    sort -u

4.5 Datenanalyse

CSV-Statistiken:

# Spalte 3: Min, Max, Durchschnitt
awk -F',' 'NR>1 {
    sum += $3; count++;
    if(min == "" || $3 < min) min = $3;
    if(max == "" || $3 > max) max = $3
} END {
    print "Min:", min
    print "Max:", max
    print "Avg:", sum/count
}' data.csv

Häufigkeit von Werten in Spalte:

awk -F',' 'NR>1 {count[$2]++} END {
    for(val in count) print count[val], val
}' data.csv | sort -rn

Gruppierte Summen:

awk -F',' 'NR>1 {sum[$1] += $3} END {
    for(group in sum) print group, sum[group]
}' sales.csv | sort -t' ' -k2 -rn

4.6 Batch-Umbenennung

Dateinamen bereinigen (Leerzeichen durch Unterstriche):

for f in *\ *; do
    mv "$f" "$(echo $f | sed 's/ /_/g')"
done

Dateien mit Datum versehen:

for f in *.txt; do
    mv "$f" "$(date +%Y%m%d)_$f"
done

Erweiterung ändern:

for f in *.jpeg; do
    mv "$f" "${f%.jpeg}.jpg"
done

# Oder mit sed
ls *.jpeg | sed 's/\(.*\)\.jpeg/mv "\1.jpeg" "\1.jpg"/' | sh

4.7 Komplexe Pipeline-Beispiele

Git: Änderungen pro Autor (letzte 30 Tage):

git log --since="30 days ago" --format='%an' | \
    sort | uniq -c | sort -rn

Apache-Log: Requests pro Minute visualisieren:

awk '{print $4}' access.log | \
    cut -d: -f1-3 | \
    uniq -c | \
    awk '{printf "%s %s\n", $2, substr("====================", 1, $1/10)}'

Wort-Frequenz-Analyse:

cat text.txt | \
    tr '[:upper:]' '[:lower:]' | \
    tr -cs '[:alpha:]' '\n' | \
    sort | uniq -c | sort -rn | head -20

JSON-Werte aggregieren (mit jq und awk):

cat data.json | \
    jq -r '.[] | "\(.category) \(.amount)"' | \
    awk '{sum[$1] += $2} END {for(c in sum) print c, sum[c]}' | \
    sort -k2 -rn

4.8 Debugging von Pipelines

Schritt-für-Schritt testen:

# Erst einzeln testen
cat log.txt | head
cat log.txt | grep "error" | head
cat log.txt | grep "error" | awk '{print $1}' | head
# ... dann erweitern

Zwischenergebnisse mit tee:

cat data.txt | \
    tee /dev/stderr | \     # Zeigt Zwischenergebnis
    grep "pattern" | \
    tee step1.txt | \       # Speichert Zwischenergebnis
    awk '{print $2}'

Pipeline mit Zeilennummern debuggen:

cat -n datei.txt | grep "muster"

Cronjobs & launchd

Automatisierte Aufgaben sind essentiell für Systemadministration, Backups, Wartung und wiederkehrende Prozesse. Unter macOS gibt es zwei Systeme: das klassische Unix-Tool cron und Apples eigenes launchd.

1 Cron-Grundlagen

cron ist der klassische Unix-Dienst für zeitgesteuerte Aufgaben. Er stammt aus den 1970er Jahren und ist auf praktisch jedem Unix/Linux-System verfügbar.

1.1 Wie cron funktioniert

  • Der cron-Daemon läuft im Hintergrund
  • Er prüft jede Minute, ob geplante Aufgaben anstehen
  • Aufgaben werden in Crontabs (cron tables) definiert
  • Jeder Benutzer kann eine eigene Crontab haben

1.2 Crontab verwalten

BefehlBeschreibung
crontab -eCrontab bearbeiten
crontab -lCrontab anzeigen
crontab -rCrontab löschen
crontab -u user -eCrontab eines anderen Benutzers bearbeiten (root)

Erste Verwendung:

crontab -e

Dies öffnet den Standard-Editor (meist vim oder nano). Um den Editor zu ändern:

export EDITOR=nano
crontab -e

1.3 Speicherorte

PfadBeschreibung
/var/at/tabs/Benutzer-Crontabs (macOS)
/etc/crontabSystem-Crontab
/etc/cron.d/Zusätzliche System-Crontabs
/etc/cron.daily/Täglich ausgeführte Skripte
/etc/cron.weekly/Wöchentlich ausgeführte Skripte
/etc/cron.monthly/Monatlich ausgeführte Skripte

warning

Unter macOS existieren die /etc/cron.*-Verzeichnisse standardmäßig nicht, da Apple launchd bevorzugt.

2 Cron-Syntax & Beispiele

2.1 Grundsyntax

Eine Crontab-Zeile hat folgendes Format:

┌───────────── Minute (0-59)
│ ┌───────────── Stunde (0-23)
│ │ ┌───────────── Tag des Monats (1-31)
│ │ │ ┌───────────── Monat (1-12)
│ │ │ │ ┌───────────── Wochentag (0-7, 0 und 7 = Sonntag)
│ │ │ │ │
* * * * * Befehl

2.2 Sonderzeichen

ZeichenBedeutungBeispiel
*Jeder Wert* * * * * = jede Minute
,Liste von Werten1,15,30 = Minute 1, 15 und 30
-Bereich1-5 = Montag bis Freitag
/Schrittweite*/15 = alle 15 Einheiten

2.3 Beispiele

Zeitangaben:

AusdruckBedeutung
* * * * *Jede Minute
0 * * * *Jede Stunde (zur vollen Stunde)
0 0 * * *Täglich um Mitternacht
0 6 * * *Täglich um 6:00 Uhr
30 8 * * 1-5Mo–Fr um 8:30 Uhr
0 0 * * 0Jeden Sonntag um Mitternacht
0 0 1 * *Am 1. jeden Monats um Mitternacht
0 0 1 1 *Am 1. Januar um Mitternacht
*/15 * * * *Alle 15 Minuten
0 */2 * * *Alle 2 Stunden
0 9-17 * * 1-5Mo–Fr, stündlich von 9–17 Uhr

Vollständige Crontab-Beispiele:

# Backup jeden Tag um 2:00 Uhr
0 2 * * * /Users/max/scripts/backup.sh

# Log-Rotation jeden Sonntag um 3:00 Uhr
0 3 * * 0 /Users/max/scripts/rotate-logs.sh

# Alle 5 Minuten: System-Check
*/5 * * * * /Users/max/scripts/health-check.sh

# Werktags um 9:00: Bericht generieren
0 9 * * 1-5 /Users/max/scripts/daily-report.sh

# Am 1. und 15. jeden Monats
0 0 1,15 * * /Users/max/scripts/bi-monthly.sh

# Nur im Januar, täglich um 6:00
0 6 * 1 * /Users/max/scripts/january-task.sh

2.4 Spezielle Strings

Einige cron-Implementierungen unterstützen lesbare Kürzel:

StringÄquivalentBedeutung
@rebootBei Systemstart
@yearly0 0 1 1 *Jährlich
@monthly0 0 1 * *Monatlich
@weekly0 0 * * 0Wöchentlich
@daily0 0 * * *Täglich
@hourly0 * * * *Stündlich
@daily /Users/max/scripts/backup.sh
@reboot /Users/max/scripts/startup.sh

macOS-Kompatibilität:

macOS verwendet Vixie cron (BSD-basiert). Die Unterstützung der speziellen Strings:

StringmacOSAnmerkung
@yearlyFunktioniert
@monthlyFunktioniert
@weeklyFunktioniert
@dailyFunktioniert
@hourlyFunktioniert
@rebootNicht zuverlässig

Problem mit @reboot: Unter macOS startet der cron-Daemon erst nach dem Benutzer-Login, nicht beim Systemstart. Jobs mit @reboot werden daher nicht ausgeführt oder nur beim ersten Login nach einem Neustart.

Lösung für Startup-Tasks: Verwende stattdessen launchd mit RunAtLoad:

<key>RunAtLoad</key>
<true/>

Siehe [[#4 Erstellung von .plist-Jobs|Erstellung von .plist-Jobs]] für Details.

2.5 Umgebungsvariablen

Cron führt Befehle mit einer minimalen Umgebung aus. Wichtige Variablen sollten definiert werden:

# Umgebungsvariablen setzen
SHELL=/bin/zsh
PATH=/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin
HOME=/Users/max
MAILTO=max@example.com

# Jobs
0 * * * * /Users/max/scripts/task.sh

Wichtig: Der PATH in cron ist sehr eingeschränkt. Entweder:

  1. Vollständige Pfade zu Programmen verwenden
  2. PATH in der Crontab setzen
  3. PATH im Skript selbst setzen
#!/bin/zsh
# Am Anfang des Skripts:
export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"

2.6 Ausgabe und Logging

Standardmäßig wird die Ausgabe per E-Mail gesendet (falls konfiguriert). Alternativen:

# Ausgabe in Datei
0 * * * * /script.sh >> /var/log/script.log 2>&1

# Ausgabe verwerfen
0 * * * * /script.sh > /dev/null 2>&1

# Nur Fehler loggen
0 * * * * /script.sh >> /var/log/script.log 2>&1

# Mit Zeitstempel
0 * * * * /script.sh 2>&1 | while read line; do echo "$(date): $line"; done >> /var/log/script.log

Logging-Wrapper-Skript:

#!/bin/zsh
# /Users/max/scripts/run-with-log.sh

LOGFILE="/var/log/cron-jobs.log"
SCRIPT="$1"
shift

echo "=== $(date '+%Y-%m-%d %H:%M:%S') - Start: $SCRIPT ===" >> "$LOGFILE"
"$SCRIPT" "$@" >> "$LOGFILE" 2>&1
EXIT_CODE=$?
echo "=== $(date '+%Y-%m-%d %H:%M:%S') - Ende: $SCRIPT (Exit: $EXIT_CODE) ===" >> "$LOGFILE"
echo "" >> "$LOGFILE"

Verwendung in Crontab:

0 * * * * /Users/max/scripts/run-with-log.sh /Users/max/scripts/task.sh

2.7 Häufige Probleme

1. Skript läuft nicht:

# Skript ausführbar machen
chmod +x /Users/max/scripts/script.sh

# Shebang prüfen
head -1 /Users/max/scripts/script.sh
# Sollte sein: #!/bin/zsh oder #!/bin/bash

2. Befehl nicht gefunden:

# FALSCH: brew ist nicht im PATH
0 * * * * brew update

# RICHTIG: Vollständiger Pfad
0 * * * * /opt/homebrew/bin/brew update

3. Berechtigungsprobleme:

# cron-Logs prüfen (macOS)
log show --predicate 'subsystem == "com.apple.cron"' --last 1h

3 launchd unter macOS

launchd ist Apples Init-System und Dienst-Manager. Es ist leistungsfähiger als cron und der empfohlene Weg für geplante Aufgaben unter macOS.

3.1 Konzepte

Agents vs. Daemons:

TypLäuft alsSpeicherortBeschreibung
User AgentAktueller Benutzer~/Library/LaunchAgents/Benutzer-spezifische Aufgaben
Global AgentAktueller Benutzer/Library/LaunchAgents/Für alle Benutzer, aber im Benutzerkontext
Global Daemonroot/Library/LaunchDaemons/Systemweite Dienste
System Daemonroot/System/Library/LaunchDaemons/macOS-Systemdienste (nicht bearbeiten!)

Für die meisten Benutzeraufgaben: ~/Library/LaunchAgents/

3.2 launchctl – Das Verwaltungstool

BefehlBeschreibung
launchctl listAlle geladenen Jobs anzeigen
`launchctl listgrep LABEL`
launchctl load FILE.plistJob laden und aktivieren
launchctl unload FILE.plistJob deaktivieren und entladen
launchctl start LABELJob sofort ausführen
launchctl stop LABELJob stoppen
launchctl kickstart gui/$(id -u)/LABELJob neu starten (moderne Syntax)

Moderne Syntax (ab macOS 10.10):

# Benutzer-Agent laden
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.user.task.plist

# Benutzer-Agent entladen
launchctl bootout gui/$(id -u)/com.user.task

# System-Daemon laden (als root)
sudo launchctl bootstrap system /Library/LaunchDaemons/com.company.daemon.plist

3.3 Job-Status prüfen

# Alle Jobs auflisten
launchctl list

# Bestimmten Job finden
launchctl list | grep -i backup

# Job-Details anzeigen (moderne Syntax)
launchctl print gui/$(id -u)/com.user.backup

# Fehler prüfen
launchctl error <error_code>

4 Erstellung von .plist-Jobs

Property List (.plist) Dateien definieren launchd-Jobs im XML-Format.

4.1 Grundstruktur

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.benutzername.jobname</string>

    <key>ProgramArguments</key>
    <array>
        <string>/pfad/zum/skript.sh</string>
    </array>

    <!-- Zeitplan oder Auslöser hier -->

</dict>
</plist>

4.2 Wichtige Schlüssel

Identifikation:

SchlüsselTypBeschreibung
LabelStringEindeutiger Bezeichner (erforderlich)
ProgramStringAuszuführendes Programm
ProgramArgumentsArrayProgramm + Argumente

Zeitsteuerung:

SchlüsselTypBeschreibung
StartIntervalIntegerIntervall in Sekunden
StartCalendarIntervalDict/ArrayKalenderbasierte Ausführung

Auslöser:

SchlüsselTypBeschreibung
RunAtLoadBooleanBei Laden ausführen
WatchPathsArrayBei Dateiänderung ausführen
QueueDirectoriesArrayAusführen wenn Dateien im Ordner
StartOnMountBooleanBei Volume-Mount ausführen

Umgebung:

SchlüsselTypBeschreibung
WorkingDirectoryStringArbeitsverzeichnis
EnvironmentVariablesDictUmgebungsvariablen
UserNameStringBenutzer (nur Daemons)
GroupNameStringGruppe (nur Daemons)

Ausgabe:

SchlüsselTypBeschreibung
StandardOutPathStringStdout in Datei
StandardErrorPathStringStderr in Datei

Verhalten:

SchlüsselTypBeschreibung
KeepAliveBoolean/DictProzess am Leben halten
ThrottleIntervalIntegerMindestzeit zwischen Neustarts
DisabledBooleanJob deaktivieren

4.3 StartCalendarInterval

Ähnlich wie cron-Syntax, aber als Dictionary:

SchlüsselWertebereich
Minute0–59
Hour0–23
Day1–31
Weekday0–7 (0 und 7 = Sonntag)
Month1–12

Beispiele:

<!-- Täglich um 6:30 -->
<key>StartCalendarInterval</key>
<dict>
    <key>Hour</key>
    <integer>6</integer>
    <key>Minute</key>
    <integer>30</integer>
</dict>

<!-- Jeden Montag um 9:00 -->
<key>StartCalendarInterval</key>
<dict>
    <key>Weekday</key>
    <integer>1</integer>
    <key>Hour</key>
    <integer>9</integer>
    <key>Minute</key>
    <integer>0</integer>
</dict>

<!-- Mehrere Zeitpunkte (Array) -->
<key>StartCalendarInterval</key>
<array>
    <dict>
        <key>Hour</key>
        <integer>9</integer>
        <key>Minute</key>
        <integer>0</integer>
    </dict>
    <dict>
        <key>Hour</key>
        <integer>17</integer>
        <key>Minute</key>
        <integer>0</integer>
    </dict>
</array>

4.4 Vollständige Beispiele

Beispiel 1: Tägliches Backup um 2:00 Uhr

Datei: ~/Library/LaunchAgents/com.user.backup.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.user.backup</string>

    <key>ProgramArguments</key>
    <array>
        <string>/Users/max/scripts/backup.sh</string>
    </array>

    <key>StartCalendarInterval</key>
    <dict>
        <key>Hour</key>
        <integer>2</integer>
        <key>Minute</key>
        <integer>0</integer>
    </dict>

    <key>StandardOutPath</key>
    <string>/Users/max/logs/backup.log</string>

    <key>StandardErrorPath</key>
    <string>/Users/max/logs/backup-error.log</string>

    <key>WorkingDirectory</key>
    <string>/Users/max</string>

    <key>EnvironmentVariables</key>
    <dict>
        <key>PATH</key>
        <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
    </dict>
</dict>
</plist>

Beispiel 2: Alle 30 Minuten ausführen

Datei: ~/Library/LaunchAgents/com.user.sync.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.user.sync</string>

    <key>ProgramArguments</key>
    <array>
        <string>/Users/max/scripts/sync.sh</string>
    </array>

    <key>StartInterval</key>
    <integer>1800</integer>  <!-- 30 Minuten = 1800 Sekunden -->

    <key>RunAtLoad</key>
    <true/>

    <key>StandardOutPath</key>
    <string>/Users/max/logs/sync.log</string>

    <key>StandardErrorPath</key>
    <string>/Users/max/logs/sync.log</string>
</dict>
</plist>

Beispiel 3: Bei Dateiänderung ausführen (WatchPaths)

Datei: ~/Library/LaunchAgents/com.user.watcher.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.user.watcher</string>

    <key>ProgramArguments</key>
    <array>
        <string>/Users/max/scripts/process-downloads.sh</string>
    </array>

    <key>WatchPaths</key>
    <array>
        <string>/Users/max/Downloads</string>
    </array>

    <key>StandardOutPath</key>
    <string>/Users/max/logs/watcher.log</string>

    <key>StandardErrorPath</key>
    <string>/Users/max/logs/watcher.log</string>
</dict>
</plist>

Beispiel 4: Bei Login ausführen

Datei: ~/Library/LaunchAgents/com.user.startup.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.user.startup</string>

    <key>ProgramArguments</key>
    <array>
        <string>/Users/max/scripts/startup.sh</string>
    </array>

    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>

Beispiel 5: Dienst dauerhaft laufen lassen (KeepAlive)

Datei: ~/Library/LaunchAgents/com.user.server.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.user.server</string>

    <key>ProgramArguments</key>
    <array>
        <string>/Users/max/scripts/server.sh</string>
    </array>

    <key>RunAtLoad</key>
    <true/>

    <key>KeepAlive</key>
    <true/>

    <key>ThrottleInterval</key>
    <integer>10</integer>

    <key>StandardOutPath</key>
    <string>/Users/max/logs/server.log</string>

    <key>StandardErrorPath</key>
    <string>/Users/max/logs/server.log</string>
</dict>
</plist>

4.5 Job aktivieren

# 1. Datei erstellen/bearbeiten
nano ~/Library/LaunchAgents/com.user.backup.plist

# 2. Syntax prüfen
plutil -lint ~/Library/LaunchAgents/com.user.backup.plist

# 3. Job laden
launchctl load ~/Library/LaunchAgents/com.user.backup.plist

# 4. Prüfen ob geladen
launchctl list | grep com.user.backup

# 5. Manuell testen
launchctl start com.user.backup

4.6 Job deaktivieren

# Job entladen
launchctl unload ~/Library/LaunchAgents/com.user.backup.plist

# Job deaktivieren (bleibt entladen nach Neustart)
launchctl unload -w ~/Library/LaunchAgents/com.user.backup.plist

# Oder: Disabled-Key in plist setzen

4.7 Fehlersuche

# plist-Syntax prüfen
plutil -lint ~/Library/LaunchAgents/com.user.task.plist

# Geladene Jobs anzeigen
launchctl list | grep com.user

# Exit-Status prüfen (0 = OK, sonst Fehler)
launchctl list | grep com.user.task
# Ausgabe: PID  Status  Label
#          -    0       com.user.task  (Status 0 = OK)
#          -    1       com.user.task  (Status 1 = Fehler)

# System-Log prüfen
log show --predicate 'subsystem == "com.apple.xpc.launchd"' --last 1h | grep com.user

# Ausgabe-Logs prüfen
tail -f ~/logs/task.log

Häufige Fehler:

StatusBedeutung
0Erfolgreich beendet
1Allgemeiner Fehler
78Konfigurationsfehler
126Befehl nicht ausführbar
127Befehl nicht gefunden

5 Unterschiede Cron vs. launchd

5.1 Vergleichstabelle

Merkmalcronlaunchd
HerkunftUnix (1970er)Apple (2005)
KonfigurationTextdatei (crontab)XML (.plist)
SyntaxEinfach, kompaktVerbose, aber flexibel
ZeitsteuerungMinutengenauSekunden möglich
IntervalleNur über ZeitpunkteNative Intervall-Unterstützung
AuslöserNur ZeitZeit, Dateien, Netzwerk, etc.
Verpasste JobsWerden übersprungenWerden nachgeholt
UmgebungMinimalKonfigurierbar
LoggingManuellIntegriert
Dienst-ManagementNicht möglichVollständig (KeepAlive)
macOS-IntegrationBasicVollständig
PortabilitätHoch (alle Unix)Nur macOS

5.2 Wann cron verwenden?

cron ist besser für:

  • Einfache, zeitbasierte Aufgaben
  • Portabilität (Skripte auch auf Linux nutzbar)
  • Schnelle, unkomplizierte Einrichtung
  • Erfahrene Unix-Nutzer

Beispiel-Anwendungsfälle:

# Einfaches tägliches Backup
0 2 * * * /Users/max/scripts/backup.sh

# Stündlicher Check
0 * * * * /Users/max/scripts/check.sh

5.3 Wann launchd verwenden?

launchd ist besser für:

  • macOS-spezifische Aufgaben
  • Reaktion auf Ereignisse (Dateiänderungen, Netzwerk)
  • Dienste die dauerhaft laufen sollen
  • Bessere Fehlerbehandlung
  • Integration mit macOS-Funktionen (Power Management, etc.)
  • Wenn verpasste Jobs nachgeholt werden sollen

Beispiel-Anwendungsfälle:

  • Ordner überwachen und bei Änderung reagieren
  • Dienst der nach Absturz neu startet
  • Task der nur bei Netzwerkverbindung läuft

5.4 Entscheidungshilfe

Aufgabe                                 → Wahl
────────────────────────────────────────────────
Einfache Zeitsteuerung                 → cron
Reaktion auf Dateiänderungen           → launchd
Dauerlaufenden Dienst                  → launchd
Portabilität zu Linux                  → cron
Nachholung verpasster Jobs             → launchd
Schnelle Einrichtung                   → cron
macOS-Integration (Sleep/Wake)         → launchd
Komplexe Bedingungen                   → launchd

5.5 Migration cron → launchd

cron:

30 2 * * * /Users/max/scripts/backup.sh

Äquivalentes launchd:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.user.backup</string>

    <key>ProgramArguments</key>
    <array>
        <string>/Users/max/scripts/backup.sh</string>
    </array>

    <key>StartCalendarInterval</key>
    <dict>
        <key>Hour</key>
        <integer>2</integer>
        <key>Minute</key>
        <integer>30</integer>
    </dict>
</dict>
</plist>

5.6 Beide parallel nutzen

Es ist möglich, beide Systeme gleichzeitig zu verwenden:

  • cron für einfache, portable Aufgaben
  • launchd für macOS-spezifische Anforderungen

Tipp: Dokumentieren, welche Aufgaben wo konfiguriert sind:

# Alle aktiven Jobs anzeigen
echo "=== Cron Jobs ==="
crontab -l

echo ""
echo "=== LaunchAgents ==="
ls ~/Library/LaunchAgents/

echo ""
echo "=== Geladene Jobs ==="
launchctl list | grep com.user

5.7 Empfehlung

Für neue macOS-Projekte wird launchd empfohlen:

  1. Bessere Integration ins System
  2. Mehr Flexibilität bei Auslösern
  3. Zuverlässigere Ausführung
  4. Bessere Logging-Möglichkeiten
  5. Von Apple unterstützt und gepflegt

cron bleibt eine gute Wahl für:

  1. Einfache, zeitbasierte Tasks
  2. Cross-Platform-Kompatibilität
  3. Schnelle Einrichtung ohne XML

tmux & screen

Terminal-Multiplexer ermöglichen mehrere Terminal-Sitzungen in einem Fenster. Sie sind unverzichtbar für Remote-Arbeit, da Sessions auch nach Verbindungsabbruch weiterlaufen.

1 tmux Grundlagen

tmux (Terminal Multiplexer) ist der moderne Standard für Terminal-Multiplexing. Es ist leistungsfähiger und aktiver entwickelt als das ältere screen.

1.1 Installation

# macOS (Homebrew)
brew install tmux

# Version prüfen
tmux -V

1.2 Konzepte

tmux organisiert sich in drei Ebenen:

Server
  └── Session (benannte Sitzung)
        └── Window (Tab/Fenster)
              └── Pane (geteilter Bereich)
BegriffBeschreibung
ServerHintergrundprozess, verwaltet alle Sessions
SessionBenannte Arbeitsumgebung mit mehreren Windows
WindowEinzelnes Terminal (wie ein Tab)
PaneGeteilter Bereich innerhalb eines Windows

1.3 Erste Schritte

# Neue Session starten
tmux

# Neue benannte Session
tmux new -s arbeit

# Session verlassen (läuft weiter)
# Prefix + d  (Ctrl+b, dann d)

# Sessions auflisten
tmux ls

# Zu Session verbinden
tmux attach -t arbeit
tmux a -t arbeit          # Kurzform

# Letzte Session
tmux attach
tmux a

1.4 Prefix-Taste

Alle tmux-Befehle beginnen mit dem Prefix (Standard: Ctrl+b), gefolgt von einer Taste.

Ctrl+b, dann Befehlstaste

Beispiel: Um ein neues Fenster zu erstellen: Ctrl+b, loslassen, dann c.

Notation in dieser Referenz:

  • Prefix + x bedeutet: Ctrl+b drücken, loslassen, dann x drücken
  • Prefix kann in der Konfiguration geändert werden (oft zu Ctrl+a)

2 Fenster, Panes, Sessions

2.1 Session-Befehle

Tastenkürzel (innerhalb tmux):

KürzelBeschreibung
Prefix + dSession detachen (verlassen)
Prefix + sSession-Liste anzeigen
Prefix + $Session umbenennen
Prefix + (Vorherige Session
Prefix + )Nächste Session

Kommandozeile:

# Neue Session
tmux new -s name

# Mit bestimmtem Startverzeichnis
tmux new -s projekt -c ~/projekte/webapp

# Session beenden
tmux kill-session -t name

# Alle Sessions beenden
tmux kill-server

# Session umbenennen
tmux rename-session -t alt neu

2.2 Window-Befehle (Fenster)

Windows sind wie Tabs innerhalb einer Session.

Tastenkürzel:

KürzelBeschreibung
Prefix + cNeues Window erstellen
Prefix + ,Window umbenennen
Prefix + &Window schließen (mit Bestätigung)
Prefix + nNächstes Window
Prefix + pVorheriges Window
Prefix + 0-9Zu Window 0–9 wechseln
Prefix + wWindow-Liste anzeigen
Prefix + lLetztes Window
Prefix + 'Window nach Nummer auswählen
Prefix + fWindow nach Name suchen

Kommandozeile:

# Neues Window in bestehender Session
tmux new-window -t session:

# Window mit Name
tmux new-window -t session: -n "logs"

# Window schließen
tmux kill-window -t session:2

2.3 Pane-Befehle (Bereiche)

Panes teilen ein Window in mehrere Bereiche.

Tastenkürzel:

KürzelBeschreibung
Prefix + %Vertikal teilen (links/rechts)
Prefix + "Horizontal teilen (oben/unten)
Prefix + xPane schließen (mit Bestätigung)
Prefix + oZum nächsten Pane wechseln
Prefix + ;Zum letzten Pane wechseln
Prefix + PfeiltastenZu Pane in Richtung wechseln
Prefix + qPane-Nummern anzeigen
Prefix + q + 0-9Zu Pane mit Nummer wechseln
Prefix + zPane zoomen (Vollbild toggle)
Prefix + !Pane in eigenes Window verschieben
Prefix + {Pane nach links/oben tauschen
Prefix + }Pane nach rechts/unten tauschen
Prefix + SpaceZwischen Layouts wechseln

Pane-Größe ändern:

KürzelBeschreibung
Prefix + Ctrl+PfeiltasteGröße in kleinen Schritten
Prefix + Alt+PfeiltasteGröße in großen Schritten

Oder im Command-Mode (Prefix + :):

resize-pane -D 10    # 10 Zeilen nach unten
resize-pane -U 5     # 5 Zeilen nach oben
resize-pane -L 10    # 10 Spalten nach links
resize-pane -R 10    # 10 Spalten nach rechts

2.4 Layouts

tmux bietet vordefinierte Layouts:

LayoutBeschreibung
even-horizontalAlle Panes nebeneinander, gleiche Breite
even-verticalAlle Panes untereinander, gleiche Höhe
main-horizontalEin großes Pane oben, Rest unten
main-verticalEin großes Pane links, Rest rechts
tiledAlle Panes gleichmäßig verteilt
# Layout wählen
Prefix + Space              # Durch Layouts rotieren
Prefix + Alt+1              # even-horizontal
Prefix + Alt+2              # even-vertical
Prefix + Alt+3              # main-horizontal
Prefix + Alt+4              # main-vertical
Prefix + Alt+5              # tiled

2.5 Copy-Mode

Der Copy-Mode ermöglicht Scrollen und Textauswahl.

KürzelBeschreibung
Prefix + [Copy-Mode starten
q oder EscapeCopy-Mode beenden
Pfeiltasten / hjklNavigation
Ctrl+u / Ctrl+dHalbe Seite hoch/runter
g / GAnfang / Ende
/Vorwärts suchen
?Rückwärts suchen
n / NNächster / Vorheriger Treffer
SpaceAuswahl starten
EnterAuswahl kopieren und beenden
Prefix + ]Einfügen

Mit vi-Modus (empfohlen):

# In ~/.tmux.conf
setw -g mode-keys vi

# Dann im Copy-Mode:
# v     = Auswahl starten
# y     = Kopieren
# V     = Zeilenweise auswählen

2.6 Command-Mode

Der Command-Mode erlaubt tmux-Befehle direkt einzugeben:

Prefix + :

Nützliche Befehle:

:new-session -s name           # Neue Session
:kill-session                  # Aktuelle Session beenden
:source ~/.tmux.conf           # Konfiguration neu laden
:list-keys                     # Alle Tastenkürzel anzeigen
:set -g option value           # Option setzen
:setw -g option value          # Window-Option setzen

3 Konfiguration

Die tmux-Konfiguration liegt in ~/.tmux.conf.

3.1 Grundlegende Einstellungen

# ~/.tmux.conf

# Prefix ändern (Ctrl+a statt Ctrl+b)
unbind C-b
set -g prefix C-a
bind C-a send-prefix

# Schnelleres Escape (wichtig für vim)
set -sg escape-time 0

# History vergrößern
set -g history-limit 50000

# Nummerierung bei 1 beginnen
set -g base-index 1
setw -g pane-base-index 1

# Automatisch umnummerieren
set -g renumber-windows on

# Terminal-Farben
set -g default-terminal "screen-256color"
set -ga terminal-overrides ",xterm-256color:Tc"

# Maus aktivieren
set -g mouse on

# Vi-Modus für Copy-Mode
setw -g mode-keys vi

# Focus-Events (für vim etc.)
set -g focus-events on

3.2 Tastenkürzel anpassen

# Intuitivere Split-Befehle
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"
unbind '"'
unbind %

# Neues Window im aktuellen Pfad
bind c new-window -c "#{pane_current_path}"

# Pane-Navigation mit Alt+Pfeiltasten (ohne Prefix)
bind -n M-Left select-pane -L
bind -n M-Right select-pane -R
bind -n M-Up select-pane -U
bind -n M-Down select-pane -D

# Window-Navigation mit Shift+Pfeiltasten
bind -n S-Left previous-window
bind -n S-Right next-window

# Konfiguration neu laden
bind r source-file ~/.tmux.conf \; display "Config reloaded!"

# Panes synchronisieren (gleiche Eingabe in alle Panes)
bind S setw synchronize-panes

3.3 Statusleiste anpassen

# Statusleiste aktivieren
set -g status on

# Position
set -g status-position bottom

# Update-Intervall (Sekunden)
set -g status-interval 5

# Farben
set -g status-style 'bg=#333333 fg=#ffffff'

# Links: Session-Name
set -g status-left '#[fg=#000000,bg=#ffffff] #S #[default] '
set -g status-left-length 30

# Rechts: Datum und Uhrzeit
set -g status-right '#[fg=#ffffff] %Y-%m-%d %H:%M '
set -g status-right-length 50

# Window-Liste
set -g status-justify left

# Aktives Window hervorheben
setw -g window-status-current-style 'fg=#000000 bg=#00ff00 bold'
setw -g window-status-current-format ' #I:#W#F '

# Inaktive Windows
setw -g window-status-style 'fg=#888888'
setw -g window-status-format ' #I:#W#F '

3.4 Pane-Darstellung

# Aktiver Pane-Rahmen
set -g pane-active-border-style 'fg=#00ff00'

# Inaktiver Pane-Rahmen
set -g pane-border-style 'fg=#555555'

# Pane-Nummern-Anzeige
set -g display-panes-time 2000
set -g display-panes-colour '#555555'
set -g display-panes-active-colour '#00ff00'

3.5 Vollständige Beispielkonfiguration

# ~/.tmux.conf - Empfohlene Konfiguration

# === Grundeinstellungen ===
set -g prefix C-a                    # Prefix: Ctrl+a
unbind C-b
bind C-a send-prefix

set -sg escape-time 0                # Kein Delay nach Escape
set -g history-limit 50000           # Große History
set -g base-index 1                  # Windows ab 1 nummerieren
setw -g pane-base-index 1            # Panes ab 1 nummerieren
set -g renumber-windows on           # Automatisch umnummerieren
set -g mouse on                      # Maus aktivieren
setw -g mode-keys vi                 # Vi-Modus
set -g focus-events on               # Focus-Events

# === Terminal ===
set -g default-terminal "screen-256color"
set -ga terminal-overrides ",*256col*:Tc"

# === Tastenkürzel ===
# Splits (im aktuellen Verzeichnis)
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"
bind c new-window -c "#{pane_current_path}"

# Pane-Navigation (Alt+Pfeiltasten)
bind -n M-Left select-pane -L
bind -n M-Right select-pane -R
bind -n M-Up select-pane -U
bind -n M-Down select-pane -D

# Pane-Größe (Prefix + Shift+Pfeiltasten)
bind -r H resize-pane -L 5
bind -r J resize-pane -D 5
bind -r K resize-pane -U 5
bind -r L resize-pane -R 5

# Window-Navigation
bind -n S-Left previous-window
bind -n S-Right next-window

# Reload
bind r source-file ~/.tmux.conf \; display "Reloaded!"

# Copy-Mode (vi-style)
bind -T copy-mode-vi v send-keys -X begin-selection
bind -T copy-mode-vi y send-keys -X copy-selection-and-cancel
bind -T copy-mode-vi r send-keys -X rectangle-toggle

# macOS Clipboard
bind -T copy-mode-vi y send-keys -X copy-pipe-and-cancel "pbcopy"

# === Statusleiste ===
set -g status on
set -g status-position bottom
set -g status-interval 5
set -g status-style 'bg=#1a1a1a fg=#808080'

set -g status-left '#[fg=#1a1a1a,bg=#87d700,bold] #S #[default] '
set -g status-left-length 30

set -g status-right '#[fg=#808080] %d.%m.%Y #[fg=#c0c0c0]%H:%M '
set -g status-right-length 50

setw -g window-status-format ' #I:#W '
setw -g window-status-current-format '#[fg=#1a1a1a,bg=#87d700,bold] #I:#W '

# === Panes ===
set -g pane-border-style 'fg=#404040'
set -g pane-active-border-style 'fg=#87d700'

# === Messages ===
set -g message-style 'bg=#87d700 fg=#1a1a1a bold'
set -g display-time 2000

3.6 Konfiguration neu laden

# Innerhalb tmux
Prefix + :
source ~/.tmux.conf

# Oder mit Tastenkürzel (wenn konfiguriert)
Prefix + r

# Von außerhalb
tmux source-file ~/.tmux.conf

4 screen-Vergleich

screen ist der ältere Terminal-Multiplexer (seit 1987). Es ist auf vielen Systemen vorinstalliert.

4.1 screen Grundlagen

# Neue Session
screen

# Benannte Session
screen -S arbeit

# Session verlassen (detach)
Ctrl+a, dann d

# Sessions auflisten
screen -ls

# Wieder verbinden
screen -r arbeit

# Verbinden (auch wenn attached)
screen -x arbeit

4.2 screen-Befehle

KürzelBeschreibung
Ctrl+a dDetach
Ctrl+a cNeues Window
Ctrl+a nNächstes Window
Ctrl+a pVorheriges Window
Ctrl+a "Window-Liste
Ctrl+a 0-9Zu Window wechseln
Ctrl+a AWindow umbenennen
Ctrl+a kWindow beenden
Ctrl+a SHorizontal teilen
`Ctrl+a`
Ctrl+a TabZwischen Regionen wechseln
Ctrl+a XAktuelle Region schließen
Ctrl+a [Copy-Mode
Ctrl+a ]Einfügen
Ctrl+a ?Hilfe

4.3 screen-Konfiguration

Die Konfiguration liegt in ~/.screenrc:

# ~/.screenrc

# Startmeldung deaktivieren
startup_message off

# Scrollback-Buffer
defscrollback 10000

# Visual Bell statt Audio
vbell on

# Statusleiste
hardstatus alwayslastline
hardstatus string '%{= kG}[ %{G}%H %{g}][%= %{= kw}%?%-Lw%?%{r}(%{W}%n*%f%t%?(%u)%?%{r})%{w}%?%+Lw%?%?%= %{g}][%{B} %m-%d %{W}%c %{g}]'

# Shell
shell -$SHELL

4.4 Vergleich tmux vs. screen

Merkmaltmuxscreen
Erste Version20071987
Aktive EntwicklungJaMinimal
VorinstalliertSeltenOft
PrefixCtrl+bCtrl+a
Vertikale SplitsNativeMit Patch
ScriptingSehr gutBegrenzt
KonfigurationFlexibelEinfach
StatusleisteSehr anpassbarBegrenzt
Pane-HandlingAusgezeichnetRudimentär
Copy-ModeVi/EmacsEmacs-style
Session-SharingEinfachMöglich

4.5 Wann welches Tool?

tmux verwenden wenn:

  • Modernes System mit Homebrew/apt
  • Komplexe Layouts mit vielen Panes
  • Umfangreiche Anpassungen gewünscht
  • Scripting/Automatisierung benötigt

screen verwenden wenn:

  • Auf älteren/minimalen Systemen arbeiten
  • Kein tmux installiert und keine Root-Rechte
  • Einfache Anforderungen (nur Sessions)
  • Gewohnte Umgebung

Empfehlung: Auf eigenen Systemen tmux installieren und verwenden. screen-Kenntnisse sind nützlich für Server ohne tmux.

5 Remote-Session-Management

Der Hauptvorteil von Terminal-Multiplexern: Sessions überleben Verbindungsabbrüche.

5.1 Typischer SSH-Workflow

# 1. Mit Server verbinden
ssh user@server

# 2. tmux starten oder verbinden
tmux new -s arbeit
# oder
tmux attach -t arbeit

# 3. Arbeiten...

# 4. Sauber trennen (Session läuft weiter)
Prefix + d

# 5. SSH beenden
exit

# 6. Später wieder verbinden
ssh user@server
tmux attach -t arbeit
# Alles ist noch da!

5.2 Automatische tmux-Session

In ~/.bashrc oder ~/.zshrc auf dem Server:

# Automatisch tmux starten bei SSH-Login
if [-z "$TMUX"](-z%20"$TMUX".md) && [-n "$SSH_CONNECTION"](-n%20"$SSH_CONNECTION".md); then
    tmux attach -t default || tmux new -s default
fi

Vorsicht: Kann Probleme bei SCP/SFTP verursachen. Besser:

# Nur bei interaktiver Shell
if [-z "$TMUX"](-z%20"$TMUX".md) && [-n "$SSH_CONNECTION"](-n%20"$SSH_CONNECTION".md) && [$- == *i*]($-%20==%20*i*.md); then
    tmux attach -t default 2>/dev/null || tmux new -s default
fi

5.3 SSH-Verbindung in tmux

# Lokale tmux-Session
tmux new -s remote-arbeit

# Innerhalb tmux: SSH-Verbindung
ssh user@server

# Wenn Verbindung abbricht: tmux-Session lokal intakt
# Einfach erneut SSH starten

5.4 Mehrere Server verwalten

# Session für jeden Server
tmux new -s server1 -d "ssh user@server1"
tmux new -s server2 -d "ssh user@server2"

# Zwischen Sessions wechseln
Prefix + s

# Oder: Ein Window pro Server
tmux new -s servers
tmux new-window -t servers -n "web" "ssh user@webserver"
tmux new-window -t servers -n "db" "ssh user@dbserver"
tmux new-window -t servers -n "app" "ssh user@appserver"

5.5 Session-Sharing

Mehrere Benutzer können dieselbe Session sehen (Pair Programming, Support):

tmux:

# Benutzer 1: Session erstellen
tmux new -s shared

# Benutzer 2: Gleiche Session (beide sehen dasselbe)
tmux attach -t shared

# Benutzer 2: Eigene Ansicht (unabhängige Window-Auswahl)
tmux new -t shared -s user2

screen:

# Multi-User aktivieren (in .screenrc oder zur Laufzeit)
multiuser on
acladd username

# Anderer Benutzer verbindet
screen -x user/sessionname

5.6 Persistente Layouts

Session beim Start mit vordefiniertem Layout:

Script-basiert:

#!/bin/zsh
# ~/scripts/dev-session.sh

SESSION="dev"

# Prüfen ob Session existiert
tmux has-session -t $SESSION 2>/dev/null

if [ $? != 0 ]; then
    # Neue Session mit erstem Window
    tmux new-session -d -s $SESSION -n "editor"

    # Editor-Window: vim
    tmux send-keys -t $SESSION:editor "cd ~/projekt && vim" Enter

    # Zweites Window: Server
    tmux new-window -t $SESSION -n "server"
    tmux send-keys -t $SESSION:server "cd ~/projekt && npm run dev" Enter

    # Drittes Window: Terminal (gesplittet)
    tmux new-window -t $SESSION -n "term"
    tmux split-window -h -t $SESSION:term
    tmux send-keys -t $SESSION:term.1 "cd ~/projekt" Enter
    tmux send-keys -t $SESSION:term.2 "cd ~/projekt && git status" Enter

    # Zum Editor-Window wechseln
    tmux select-window -t $SESSION:editor
fi

# Zur Session verbinden
tmux attach -t $SESSION

Mit tmuxinator (empfohlen für komplexe Setups):

# Installation
brew install tmuxinator

# Neues Projekt erstellen
tmuxinator new projekt
# ~/.config/tmuxinator/projekt.yml
name: projekt
root: ~/projekt

windows:
  - editor:
      layout: main-vertical
      panes:
        - vim
        - git status
  - server:
      panes:
        - npm run dev
  - logs:
      panes:
        - tail -f logs/app.log
        - tail -f logs/error.log
# Session starten
tmuxinator start projekt

# Session beenden
tmuxinator stop projekt

5.7 tmux und SSH-Agent

SSH-Agent-Forwarding kann in tmux problematisch sein. Lösung:

# In ~/.tmux.conf
set -g update-environment "SSH_AUTH_SOCK SSH_AGENT_PID"

# Oder: Symlink-Methode in ~/.zshrc
if [-n "$SSH_AUTH_SOCK"](-n%20"$SSH_AUTH_SOCK".md) && ["$SSH_AUTH_SOCK" != "$HOME/.ssh/ssh_auth_sock"]("$SSH_AUTH_SOCK"%20!=%20"$HOME/.ssh/ssh_auth_sock".md); then
    ln -sf "$SSH_AUTH_SOCK" "$HOME/.ssh/ssh_auth_sock"
fi
export SSH_AUTH_SOCK="$HOME/.ssh/ssh_auth_sock"

5.8 Nützliche Aliase

# In ~/.zshrc

# tmux-Aliase
alias ta='tmux attach -t'
alias tl='tmux list-sessions'
alias tn='tmux new -s'
alias tk='tmux kill-session -t'

# Schnell zu Session oder neue erstellen
t() {
    if [-n "$1"](-n%20"$1".md); then
        tmux attach -t "$1" 2>/dev/null || tmux new -s "$1"
    else
        tmux attach 2>/dev/null || tmux new -s main
    fi
}

5.9 Cheatsheet

Session-Management:

tmux                         Neue Session
tmux new -s NAME             Benannte Session
tmux ls                      Sessions auflisten
tmux attach -t NAME          Zu Session verbinden
tmux kill-session -t NAME    Session beenden
Prefix + d                   Detach
Prefix + s                   Session-Auswahl

Windows:

Prefix + c                   Neues Window
Prefix + n / p               Nächstes / Vorheriges
Prefix + 0-9                 Zu Window wechseln
Prefix + ,                   Umbenennen
Prefix + &                   Schließen
Prefix + w                   Window-Liste

Panes:

Prefix + %                   Vertikal teilen
Prefix + "                   Horizontal teilen
Prefix + Pfeiltasten         Navigation
Prefix + z                   Zoom (Vollbild)
Prefix + x                   Schließen
Prefix + Space               Layout wechseln
Prefix + !                   Pane → Window

Copy-Mode:

Prefix + [                   Copy-Mode starten
q / Escape                   Beenden
Space / v                    Auswahl starten
Enter / y                    Kopieren
Prefix + ]                   Einfügen

SSH-Keys

SSH-Keys ermöglichen sichere, passwortlose Authentifizierung bei Remote-Servern. Sie sind sicherer als Passwörter und essentiell für automatisierte Workflows.

1 SSH-Keys erstellen & verwalten

1.1 Funktionsweise

SSH verwendet asymmetrische Kryptografie:

SchlüsselDateiVerbleibFunktion
Private Key~/.ssh/id_ed25519Lokal (geheim!)Zum Signieren
Public Key~/.ssh/id_ed25519.pubServerZum Verifizieren

Der Private Key verlässt niemals den lokalen Rechner. Der Public Key wird auf alle Server kopiert, zu denen man sich verbinden möchte.

1.2 Algorithmen

AlgorithmusEmpfehlungAnmerkung
Ed25519✅ EmpfohlenModern, schnell, sicher, kurze Keys
RSA⚠️ AkzeptabelMindestens 4096 Bit, für ältere Systeme
ECDSA❌ VermeidenPotenzielle Schwächen
DSA❌ VeraltetNicht mehr verwenden

1.3 Key erstellen

Ed25519 (empfohlen):

ssh-keygen -t ed25519 -C "kommentar@example.com"

RSA (für Kompatibilität):

ssh-keygen -t rsa -b 4096 -C "kommentar@example.com"

Interaktiver Dialog:

Generating public/private ed25519 key pair.
Enter file in which to save the key (/Users/max/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /Users/max/.ssh/id_ed25519
Your public key has been saved in /Users/max/.ssh/id_ed25519.pub

Optionen:

OptionBeschreibung
-t TYPEAlgorithmus (ed25519, rsa, ecdsa)
-b BITSSchlüssellänge (nur RSA: 4096)
-C "text"Kommentar (meist E-Mail)
-f DATEIAusgabedatei
-N "phrase"Passphrase (leer = keine)

Beispiele:

# Standard Ed25519 Key
ssh-keygen -t ed25519 -C "max@example.com"

# Key mit bestimmtem Namen
ssh-keygen -t ed25519 -C "github" -f ~/.ssh/github_ed25519

# Key ohne Passphrase (für Automatisierung)
ssh-keygen -t ed25519 -C "automation" -f ~/.ssh/automation -N ""

# RSA Key für ältere Systeme
ssh-keygen -t rsa -b 4096 -C "legacy-server" -f ~/.ssh/legacy_rsa

1.4 Passphrase

Die Passphrase verschlüsselt den Private Key:

Mit PassphraseOhne Passphrase
Key ist verschlüsseltKey ist unverschlüsselt
Bei Diebstahl geschütztSofort nutzbar bei Diebstahl
Muss bei Verwendung eingegeben werdenKeine Eingabe nötig
SSH-Agent speichert temporärFür Automatisierung geeignet

Empfehlung: Immer Passphrase verwenden, SSH-Agent für Komfort nutzen.

Passphrase ändern:

ssh-keygen -p -f ~/.ssh/id_ed25519

1.5 Dateien und Berechtigungen

Standard-Speicherort: ~/.ssh/

DateiBeschreibungBerechtigung
id_ed25519Private Key600 (nur Besitzer)
id_ed25519.pubPublic Key644 (lesbar)
authorized_keysErlaubte Keys600
known_hostsBekannte Server644
configSSH-Konfiguration600

Berechtigungen setzen:

# Verzeichnis
chmod 700 ~/.ssh

# Private Keys
chmod 600 ~/.ssh/id_*
chmod 600 ~/.ssh/*_ed25519
chmod 600 ~/.ssh/*_rsa

# Public Keys
chmod 644 ~/.ssh/*.pub

# Konfigurationsdateien
chmod 600 ~/.ssh/config
chmod 600 ~/.ssh/authorized_keys
chmod 644 ~/.ssh/known_hosts

SSH verweigert Keys mit zu offenen Berechtigungen!

1.6 Public Key auf Server kopieren

Methode 1: ssh-copy-id (empfohlen)

ssh-copy-id user@server

# Bestimmten Key
ssh-copy-id -i ~/.ssh/github_ed25519.pub user@server

Methode 2: Manuell

# Public Key anzeigen
cat ~/.ssh/id_ed25519.pub

# Auf Server: Key in authorized_keys einfügen
ssh user@server
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "PUBLICKEY" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

Methode 3: Einzeiler

cat ~/.ssh/id_ed25519.pub | ssh user@server "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"

1.7 Keys verwalten

Alle Keys auflisten:

ls -la ~/.ssh/

# Fingerprints anzeigen
for key in ~/.ssh/id_*; do
    [-f "$key" && "$key" != *.pub](-f%20"$key"%20&&%20"$key"%20!=%20*.pub.md) && ssh-keygen -lf "$key"
done

Key-Fingerprint anzeigen:

# MD5 (älteres Format)
ssh-keygen -l -E md5 -f ~/.ssh/id_ed25519.pub

# SHA256 (Standard)
ssh-keygen -l -f ~/.ssh/id_ed25519.pub

Public Key aus Private Key extrahieren:

ssh-keygen -y -f ~/.ssh/id_ed25519 > ~/.ssh/id_ed25519.pub

Key löschen:

# Beide Dateien entfernen
rm ~/.ssh/id_ed25519 ~/.ssh/id_ed25519.pub

# Aus authorized_keys auf Server entfernen
ssh user@server
nano ~/.ssh/authorized_keys  # Zeile entfernen

1.8 Mehrere Keys

Für verschiedene Zwecke separate Keys erstellen:

# Persönlicher Key
ssh-keygen -t ed25519 -C "personal" -f ~/.ssh/personal_ed25519

# Arbeit
ssh-keygen -t ed25519 -C "work" -f ~/.ssh/work_ed25519

# GitHub
ssh-keygen -t ed25519 -C "github" -f ~/.ssh/github_ed25519

# Automation (ohne Passphrase)
ssh-keygen -t ed25519 -C "automation" -f ~/.ssh/automation_ed25519 -N ""

Die Zuordnung erfolgt über ~/.ssh/config (siehe Abschnitt 5.2).

2 SSH-Konfiguration (~/.ssh/config)

Die SSH-Konfiguration vereinfacht Verbindungen und ermöglicht hostspezifische Einstellungen.

2.1 Grundstruktur

Host ALIAS
    Option Wert
    Option Wert

Einfaches Beispiel:

Host server
    HostName 192.168.1.100
    User admin
    Port 22

Statt ssh admin@192.168.1.100 genügt nun ssh server.

2.2 Wichtige Optionen

OptionBeschreibungBeispiel
HostAlias oder Patternserver, *.example.com
HostNameEchter Hostname/IP192.168.1.100
UserBenutzernameadmin
PortSSH-Port22, 2222
IdentityFilePrivate Key~/.ssh/work_ed25519
IdentitiesOnlyNur angegebene Keysyes
ForwardAgentAgent-Forwardingyes
ProxyJumpJump-Hostbastion
LocalForwardPort-Forwarding lokal8080 localhost:80
RemoteForwardPort-Forwarding remote9090 localhost:8080
ServerAliveIntervalKeep-alive Intervall60
ServerAliveCountMaxKeep-alive Versuche3
CompressionKomprimierungyes
AddKeysToAgentKey zum Agentyes
UseKeychainmacOS Keychainyes

2.3 Vollständige Beispielkonfiguration

# ~/.ssh/config

# === Globale Einstellungen ===
Host *
    AddKeysToAgent yes
    UseKeychain yes
    IdentitiesOnly yes
    ServerAliveInterval 60
    ServerAliveCountMax 3

# === Arbeit ===
Host work
    HostName work-server.company.com
    User max.mustermann
    IdentityFile ~/.ssh/work_ed25519

Host work-*
    User max.mustermann
    IdentityFile ~/.ssh/work_ed25519

Host work-web
    HostName 10.0.1.10

Host work-db
    HostName 10.0.1.20

Host work-app
    HostName 10.0.1.30

# === Persönlich ===
Host home
    HostName home.dyndns.org
    User max
    Port 2222
    IdentityFile ~/.ssh/personal_ed25519

Host pi
    HostName 192.168.1.50
    User pi
    IdentityFile ~/.ssh/personal_ed25519

# === GitHub ===
Host github.com
    HostName github.com
    User git
    IdentityFile ~/.ssh/github_ed25519

# Zweiter GitHub Account
Host github-work
    HostName github.com
    User git
    IdentityFile ~/.ssh/work_github_ed25519

# === GitLab ===
Host gitlab.com
    HostName gitlab.com
    User git
    IdentityFile ~/.ssh/gitlab_ed25519

# === Jump Host / Bastion ===
Host bastion
    HostName bastion.company.com
    User jump
    IdentityFile ~/.ssh/work_ed25519

Host internal-*
    ProxyJump bastion
    User admin
    IdentityFile ~/.ssh/work_ed25519

Host internal-web
    HostName 10.10.1.10

Host internal-db
    HostName 10.10.1.20

# === Port Forwarding ===
Host tunnel-db
    HostName dbserver.company.com
    User admin
    LocalForward 5432 localhost:5432
    IdentityFile ~/.ssh/work_ed25519

# === Alte/Legacy Server ===
Host legacy
    HostName old-server.example.com
    User root
    IdentityFile ~/.ssh/legacy_rsa
    PubkeyAcceptedAlgorithms +ssh-rsa
    HostkeyAlgorithms +ssh-rsa

2.4 Wildcards und Patterns

PatternBeschreibung
*Beliebige Zeichen
?Ein beliebiges Zeichen
!Negation
# Alle Hosts
Host *
    ServerAliveInterval 60

# Alle .company.com Hosts
Host *.company.com
    User admin
    IdentityFile ~/.ssh/work_ed25519

# Alle außer bestimmte
Host * !github.com !gitlab.com
    IdentityFile ~/.ssh/default_ed25519

2.5 Jump Hosts (ProxyJump)

Verbindung über einen Zwischenserver (Bastion Host):

# Klassisch (alt)
Host internal
    ProxyCommand ssh -W %h:%p bastion

# Modern (empfohlen)
Host internal
    ProxyJump bastion

Mehrere Hops:

Host deep-internal
    ProxyJump bastion,middle-server

Kommandozeile:

ssh -J bastion internal-server
ssh -J bastion,middle deep-server

2.6 Port Forwarding

Local Forward (Zugriff auf Remote-Dienst):

# Remote-Datenbank lokal verfügbar machen
Host tunnel-postgres
    HostName dbserver.com
    LocalForward 5432 localhost:5432
ssh tunnel-postgres
# Dann: psql -h localhost -p 5432

Remote Forward (Lokalen Dienst remote verfügbar):

# Lokalen Webserver remote verfügbar machen
Host expose-local
    HostName server.com
    RemoteForward 8080 localhost:3000

Dynamic Forward (SOCKS Proxy):

Host socks-proxy
    HostName server.com
    DynamicForward 1080
ssh socks-proxy
# Browser: SOCKS5 Proxy auf localhost:1080

2.7 Mehrere GitHub/GitLab Accounts

# Persönlicher GitHub Account
Host github.com
    HostName github.com
    User git
    IdentityFile ~/.ssh/github_personal

# Arbeits-GitHub Account
Host github-work
    HostName github.com
    User git
    IdentityFile ~/.ssh/github_work

Verwendung:

# Persönlich (Standard)
git clone git@github.com:user/repo.git

# Arbeit
git clone git@github-work:company/repo.git

# In bestehendem Repo Remote ändern
git remote set-url origin git@github-work:company/repo.git

2.8 Reihenfolge und Priorität

SSH liest die Config von oben nach unten. Der erste Match für jede Option gewinnt.

# RICHTIG: Spezifisch vor allgemein
Host work-server
    User admin

Host *
    User default

# FALSCH: Allgemein überschreibt spezifisch
Host *
    User default

Host work-server
    User admin  # Wird ignoriert!

Empfohlene Struktur:

  1. Spezifische Hosts
  2. Gruppen-Patterns (work-*, *.company.com)
  3. Globale Einstellungen (Host *)

3 SSH-Agent, Konfiguration und Sicherheit

3.1 SSH-Agent Grundlagen

Der SSH-Agent speichert entschlüsselte Private Keys im Speicher, sodass die Passphrase nicht bei jeder Verbindung eingegeben werden muss.

Funktionsweise:

┌─────────────────────────────────────────────────────┐
│                    SSH-Agent                        │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  │
│  │ Key 1       │  │ Key 2       │  │ Key 3       │  │
│  │ (entschl.)  │  │ (entschl.)  │  │ (entschl.)  │  │
│  └─────────────┘  └─────────────┘  └─────────────┘  │
└─────────────────────────────────────────────────────┘
        ↑                   ↑                ↑
   ssh server1         ssh server2      git push
   (kein Passwort)     (kein Passwort)  (kein Passwort)

3.2 Agent unter macOS

macOS startet den SSH-Agent automatisch. Die Integration mit der Keychain speichert Passphrasen dauerhaft.

Keychain-Integration aktivieren:

# ~/.ssh/config
Host *
    AddKeysToAgent yes
    UseKeychain yes

Key manuell zur Keychain hinzufügen:

# Key zum Agent hinzufügen (Passphrase in Keychain speichern)
ssh-add --apple-use-keychain ~/.ssh/id_ed25519

# Alle Standard-Keys laden
ssh-add --apple-load-keychain

warning

Ältere macOS-Versionen verwenden -K statt --apple-use-keychain und -A statt --apple-load-keychain.

3.3 Agent-Befehle

BefehlBeschreibung
ssh-addStandard-Keys hinzufügen
ssh-add DATEIBestimmten Key hinzufügen
ssh-add -lGeladene Keys auflisten
ssh-add -LPublic Keys der geladenen Keys
ssh-add -d DATEIKey entfernen
ssh-add -DAlle Keys entfernen
ssh-add -t SEKUNDENKey mit Zeitlimit

Beispiele:

# Key hinzufügen
ssh-add ~/.ssh/work_ed25519

# Alle Keys anzeigen
ssh-add -l

# Key für 1 Stunde hinzufügen
ssh-add -t 3600 ~/.ssh/sensitive_ed25519

# Alle Keys entfernen (Sicherheit)
ssh-add -D

3.4 Agent Forwarding

Agent Forwarding erlaubt die Nutzung lokaler Keys auf Remote-Servern, ohne die Keys dorthin zu kopieren.

Lokal          →     Server A     →     Server B
(Agent mit Keys)     (kein Key)        (kein Key)
                     nutzt lokalen     nutzt lokalen
                     Agent             Agent via A

Aktivieren:

# ~/.ssh/config
Host trusted-server
    ForwardAgent yes

Oder per Kommandozeile:

ssh -A trusted-server

caution

Agent Forwarding nur für vertrauenswürdige Server aktivieren! Ein kompromittierter Server könnte den Agent missbrauchen.

Sicherer: ProxyJump statt Agent Forwarding

Host internal
    ProxyJump bastion

ProxyJump ist sicherer, da der Zwischenserver keinen Zugriff auf den Agent erhält.

3.5 Sicherheits-Best-Practices

Key-Management:

PraxisBeschreibung
Passphrase verwendenSchützt bei Diebstahl des Keys
Separate KeysPro Zweck/Dienst eigene Keys
Ed25519 verwendenModernster, sicherster Algorithmus
Keys rotierenRegelmäßig neue Keys erstellen
Alte Keys entfernenNicht mehr benötigte Keys löschen

Berechtigungen:

# Einmalig: Alle Berechtigungen korrekt setzen
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_* ~/.ssh/config ~/.ssh/authorized_keys
chmod 644 ~/.ssh/*.pub ~/.ssh/known_hosts

authorized_keys absichern:

# Optionen vor dem Key einschränken
# In ~/.ssh/authorized_keys auf dem Server:

# Nur von bestimmter IP
from="192.168.1.100" ssh-ed25519 AAAA... user@host

# Kein Agent-Forwarding, kein PTY
no-agent-forwarding,no-pty ssh-ed25519 AAAA... automation@host

# Nur bestimmten Befehl erlauben
command="/usr/local/bin/backup.sh" ssh-ed25519 AAAA... backup@host

# Kombination
from="10.0.0.*",no-agent-forwarding,no-port-forwarding ssh-ed25519 AAAA...

Verfügbare Optionen:

OptionBeschreibung
from="PATTERN"Nur von bestimmten IPs/Hosts
command="CMD"Nur diesen Befehl ausführen
no-agent-forwardingAgent-Forwarding verbieten
no-port-forwardingPort-Forwarding verbieten
no-ptyKein Terminal (nur Befehle)
no-X11-forwardingX11-Forwarding verbieten
environment="VAR=val"Umgebungsvariable setzen

3.6 known_hosts

Die Datei ~/.ssh/known_hosts speichert Fingerprints bekannter Server und schützt vor Man-in-the-Middle-Angriffen.

Erste Verbindung:

The authenticity of host 'server.com (192.168.1.100)' can't be established.
ED25519 key fingerprint is SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
Are you sure you want to continue connecting (yes/no/[fingerprint])?

Fingerprint verifizieren:

# Auf dem Server
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub

# Vergleichen mit angezeigtem Fingerprint

Host entfernen (bei legitimem Key-Wechsel):

ssh-keygen -R server.com
ssh-keygen -R 192.168.1.100

Hashed known_hosts (mehr Privatsphäre):

# ~/.ssh/config
Host *
    HashKnownHosts yes

3.7 Server-Konfiguration (sshd_config)

Auf dem Server in /etc/ssh/sshd_config:

# Nur Key-Authentifizierung
PubkeyAuthentication yes
PasswordAuthentication no
PermitEmptyPasswords no

# Root-Login verbieten oder einschränken
PermitRootLogin no
# Oder: PermitRootLogin prohibit-password

# Nur bestimmte User erlauben
AllowUsers admin deploy

# Nur bestimmte Gruppen
AllowGroups sshusers

# Agent-Forwarding global deaktivieren
AllowAgentForwarding no

# Idle-Timeout
ClientAliveInterval 300
ClientAliveCountMax 2

# Sichere Algorithmen (modern)
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org
HostKeyAlgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com

Nach Änderungen:

# Syntax prüfen
sudo sshd -t

# Dienst neu starten
sudo systemctl restart sshd
# Oder macOS:
sudo launchctl stop com.openssh.sshd
sudo launchctl start com.openssh.sshd

3.8 Troubleshooting

Verbindung debuggen:

# Verbose-Modus
ssh -v user@server      # Level 1
ssh -vv user@server     # Level 2
ssh -vvv user@server    # Level 3 (sehr detailliert)

Häufige Probleme:

ProblemUrsacheLösung
Permission denied (publickey)Key nicht akzeptiertKey in authorized_keys prüfen
Bad permissionsFalsche Dateirechtechmod 600 ~/.ssh/id_*
Host key verification failedServer-Key geändertssh-keygen -R host
Connection refusedSSH nicht aktiv / PortFirewall, sshd-Status prüfen
Too many authentication failuresZu viele Keys probiertIdentitiesOnly yes

Agent prüfen:

# Agent läuft?
echo $SSH_AUTH_SOCK

# Keys geladen?
ssh-add -l

# Agent neu starten (falls nötig)
eval $(ssh-agent -s)
ssh-add

Berechtigungen prüfen:

# Lokal
ls -la ~/.ssh/

# Auf Server
ls -la ~/.ssh/
ls -la ~/.ssh/authorized_keys

3.9 Nützliche Aliase und Funktionen

# ~/.zshrc

# SSH-Key Fingerprints anzeigen
alias ssh-fingerprints='for key in ~/.ssh/id_*; do [-f "$key" && "$key" != *.pub](-f%20"$key"%20&&%20"$key"%20!=%20*.pub.md) && ssh-keygen -lf "$key"; done'

# Neuen Ed25519 Key erstellen
ssh-newkey() {
    local name="${1:-id_ed25519}"
    local comment="${2:-$(whoami)@$(hostname)}"
    ssh-keygen -t ed25519 -C "$comment" -f ~/.ssh/"$name"
}

# Key zu Server kopieren
ssh-copykey() {
    local key="${1:-~/.ssh/id_ed25519.pub}"
    local server="$2"
    if [-z "$server"](-z%20"$server".md); then
        echo "Usage: ssh-copykey [keyfile] server"
        return 1
    fi
    ssh-copy-id -i "$key" "$server"
}

# SSH-Verbindung testen
ssh-test() {
    ssh -o BatchMode=yes -o ConnectTimeout=5 "$1" echo "OK" 2>&1
}

3.10 Cheatsheet

Keys erstellen:

ssh-keygen -t ed25519 -C "comment"           Neuer Ed25519 Key
ssh-keygen -t ed25519 -f ~/.ssh/name         Mit bestimmtem Namen
ssh-keygen -p -f ~/.ssh/id_ed25519           Passphrase ändern
ssh-keygen -y -f private > public            Public aus Private
ssh-keygen -lf ~/.ssh/id_ed25519             Fingerprint anzeigen

Keys verteilen:

ssh-copy-id user@server                      Key auf Server kopieren
ssh-copy-id -i ~/.ssh/key.pub user@server    Bestimmten Key kopieren

Agent:

ssh-add                                      Standard-Keys laden
ssh-add ~/.ssh/key                           Bestimmten Key laden
ssh-add -l                                   Geladene Keys anzeigen
ssh-add -d ~/.ssh/key                        Key entfernen
ssh-add -D                                   Alle Keys entfernen
ssh-add --apple-use-keychain ~/.ssh/key      In macOS Keychain (neu)

Verbindung:

ssh server                                   Mit Alias verbinden
ssh -i ~/.ssh/key user@server                Mit bestimmtem Key
ssh -J bastion internal                      Über Jump-Host
ssh -L 8080:localhost:80 server              Local Port Forward
ssh -v server                                Debug-Modus

Wartung:

ssh-keygen -R hostname                       Host aus known_hosts entfernen
chmod 700 ~/.ssh                             Verzeichnis-Berechtigung
chmod 600 ~/.ssh/id_* ~/.ssh/config          Datei-Berechtigungen

ZLE / ZLE-Widgets

Der Zsh Line Editor (ZLE) ist das Herzstück der Kommandozeilen-Bearbeitung in zsh. Er ermöglicht umfangreiche Anpassungen der Eingabe, eigene Tastenkürzel und mächtige Widgets.

1 Grundlagen & Architektur

1.1 Was ist ZLE?

ZLE ist der eingebaute Zeileneditor von zsh, vergleichbar mit readline in bash. Er verarbeitet alle Tastatureingaben und stellt Funktionen bereit für:

  • Cursor-Bewegung und Textbearbeitung
  • History-Navigation
  • Tab-Completion
  • Vi- und Emacs-Modus
  • Benutzerdefinierte Widgets

1.2 Keymaps

ZLE organisiert Tastenbelegungen in Keymaps:

KeymapBeschreibung
emacsEmacs-Modus (Standard)
viinsVi-Insert-Modus
vicmdVi-Command-Modus
visualVi-Visual-Modus
isearchInkrementelle Suche
commandBefehlseingabe (nach M-x)
mainAlias für aktive Keymap

Aktuelle Keymap anzeigen:

bindkey -l          # Alle Keymaps auflisten
bindkey -M emacs    # Bindungen der emacs-Keymap
bindkey -M viins    # Bindungen des vi-Insert-Modus

Keymap wechseln:

# Emacs-Modus (Standard)
bindkey -e

# Vi-Modus
bindkey -v

1.3 Widgets

Ein Widget ist eine Funktion, die an eine Taste gebunden werden kann. ZLE stellt über 150 eingebaute Widgets bereit.

Eingebaute Widgets anzeigen:

zle -la             # Alle Widgets auflisten
zle -la | wc -l     # Anzahl
zle -la | grep hist # Widgets mit "hist" im Namen

Wichtige eingebaute Widgets:

WidgetBeschreibung
accept-lineZeile ausführen (Enter)
backward-charCursor links
forward-charCursor rechts
backward-wordWort zurück
forward-wordWort vor
beginning-of-lineZeilenanfang
end-of-lineZeilenende
backward-delete-charBackspace
delete-charDelete
kill-lineBis Zeilenende löschen
backward-kill-lineBis Zeilenanfang löschen
kill-wordWort löschen
yankEinfügen (aus Kill-Ring)
undoRückgängig
redoWiederholen
clear-screenBildschirm löschen
history-search-backwardHistory rückwärts suchen
history-search-forwardHistory vorwärts suchen
expand-or-completeTab-Completion
self-insertZeichen einfügen

1.4 Aktuelle Bindungen anzeigen

# Alle Bindungen
bindkey

# Bestimmte Taste
bindkey "^A"        # Was macht Ctrl+A?
bindkey "^[[A"      # Was macht Pfeil-Hoch?

# Widget suchen
bindkey | grep kill

1.5 Escape-Sequenzen

Tasten werden als Escape-Sequenzen dargestellt:

NotationBedeutungBeispiel
^XCtrl+X^A = Ctrl+A
^[Escape / Alt^[b = Alt+B
\eEscape (alternativ)\eb = Alt+B
`^[CSI (Terminal-Sequenz)^[[A = Pfeil hoch
^[OSS3 (Terminal-Sequenz)^[OA = Pfeil hoch (alt)

Terminal-Sequenzen ermitteln:

# Methode 1: cat
cat -v
# Dann Taste drücken, z.B. Pfeil hoch zeigt: ^[[A

# Methode 2: read
read -k
# Taste drücken

# Methode 3: Ctrl+V in zsh
# Ctrl+V drücken, dann Taste → zeigt Sequenz

Häufige Terminal-Sequenzen:

TasteSequenz
Pfeil hoch^[[A
Pfeil runter^[[B
Pfeil rechts^[[C
Pfeil links^[[D
Home^[[H oder ^[OH
End^[[F oder ^[OF
Delete^[[3~
Page Up^[[5~
Page Down^[[6~
F1–F12^[OP bis ^[[24~

1.6 ZLE-Variablen

Innerhalb eines Widgets verfügbare Variablen:

VariableBeschreibung
$BUFFERGesamte Eingabezeile
$LBUFFERText links vom Cursor
$RBUFFERText rechts vom Cursor
$CURSORCursor-Position (0-basiert)
$WIDGETName des aktuellen Widgets
$LASTWIDGETName des vorherigen Widgets
$KEYSGedrückte Tasten
$KEYMAPAktive Keymap
$PREBUFFERVorherige Zeilen (Multiline)
$REGION_ACTIVEIst Region aktiv?
$MARKPosition der Markierung

2 Eigene Widgets schreiben

2.1 Widget-Grundstruktur

# Widget-Funktion definieren
my-widget() {
    # Code hier
}

# Als Widget registrieren
zle -N my-widget

# An Taste binden
bindkey "^X^M" my-widget

2.2 Einfache Beispiele

Text einfügen:

# Aktuelles Datum einfügen
insert-date() {
    LBUFFER+=$(date +%Y-%m-%d)
}
zle -N insert-date
bindkey "^Xd" insert-date

# Timestamp einfügen
insert-timestamp() {
    LBUFFER+=$(date +"%Y-%m-%d %H:%M:%S")
}
zle -N insert-timestamp
bindkey "^Xt" insert-timestamp

Buffer manipulieren:

# Zeile in Großbuchstaben
uppercase-line() {
    BUFFER=${BUFFER:u}
}
zle -N uppercase-line
bindkey "^Xu" uppercase-line

# Zeile in Kleinbuchstaben
lowercase-line() {
    BUFFER=${BUFFER:l}
}
zle -N lowercase-line
bindkey "^Xl" lowercase-line

# Wort unter Cursor in Großbuchstaben
uppercase-word() {
    local word="${LBUFFER##* }"
    LBUFFER="${LBUFFER% *} ${word:u}"
}
zle -N uppercase-word

Sudo voranstellen:

prepend-sudo() {
    if [[ $BUFFER != sudo\ *](`.md); then
        BUFFER="sudo $BUFFER"
        CURSOR+=5
    fi
}
zle -N prepend-sudo
bindkey "^[s" prepend-sudo   # Alt+S

2.3 Mit History arbeiten

# Letzten Befehl mit sudo wiederholen
sudo-last-command() {
    BUFFER="sudo $(fc -ln -1)"
    zle accept-line
}
zle -N sudo-last-command
bindkey "^[!" sudo-last-command

# Letztes Argument des vorherigen Befehls
insert-last-arg() {
    zle insert-last-word
}
zle -N insert-last-arg
bindkey "^[." insert-last-arg   # Alt+. (oft schon belegt)

2.4 Externe Befehle einbinden

# fzf für History-Suche
fzf-history() {
    local selected
    selected=$(fc -ln 1 | fzf --tac --no-sort)
    if [-n "$selected"](-n%20"$selected".md); then
        BUFFER="$selected"
        CURSOR=$#BUFFER
    fi
    zle redisplay
}
zle -N fzf-history
bindkey "^R" fzf-history

# fzf für Dateiauswahl
fzf-file() {
    local file
    file=$(fzf)
    if [-n "$file"](-n%20"$file".md); then
        LBUFFER+="${(q)file}"  # Quoted einfügen
    fi
    zle redisplay
}
zle -N fzf-file
bindkey "^Xf" fzf-file

# fzf für Verzeichniswechsel
fzf-cd() {
    local dir
    dir=$(find . -type d 2>/dev/null | fzf)
    if [-n "$dir"](-n%20"$dir".md); then
        cd "$dir"
        zle reset-prompt
    fi
}
zle -N fzf-cd
bindkey "^Xc" fzf-cd

2.5 Clipboard-Integration

# Auswahl in Clipboard kopieren (macOS)
copy-to-clipboard() {
    echo -n "$BUFFER" | pbcopy
    zle -M "Copied to clipboard"
}
zle -N copy-to-clipboard
bindkey "^Xy" copy-to-clipboard

# Aus Clipboard einfügen
paste-from-clipboard() {
    LBUFFER+=$(pbpaste)
}
zle -N paste-from-clipboard
bindkey "^Xp" paste-from-clipboard

# Kill-Ring nach Clipboard
copy-kill-ring() {
    echo -n "$CUTBUFFER" | pbcopy
    zle -M "Kill buffer copied"
}
zle -N copy-kill-ring

2.6 Widgets mit Parametern

# Widget das andere Widgets aufruft
surround-with-quotes() {
    BUFFER="\"$BUFFER\""
    CURSOR=$#BUFFER
}
zle -N surround-with-quotes
bindkey "^X\"" surround-with-quotes

# Flexibler: Wrapper-Funktion
surround-with() {
    local open="$1" close="$2"
    BUFFER="${open}${BUFFER}${close}"
    CURSOR=$#BUFFER
}

surround-parens()   { surround-with '(' ')'; }
surround-brackets() { surround-with '[' ']'; }
surround-braces()   { surround-with '{' '}'; }
surround-single()   { surround-with "'" "'"; }
surround-double()   { surround-with '"' '"'; }

zle -N surround-parens
zle -N surround-brackets
zle -N surround-braces
zle -N surround-single
zle -N surround-double

bindkey "^X(" surround-parens
bindkey "^X[" surround-brackets
bindkey "^X{" surround-braces
bindkey "^X'" surround-single
bindkey '^X"' surround-double

2.7 Rückgabewerte und Fehlerbehandlung

# Widget mit Fehlermeldung
safe-rm() {
    if [-z "$BUFFER"](-z%20"$BUFFER".md); then
        zle -M "Buffer ist leer"
        return 1
    fi

    if ["$BUFFER" == rm\ *]("$BUFFER"%20==%20rm\%20*.md); then
        BUFFER="${BUFFER/rm /rm -i }"
        zle -M "rm → rm -i (interaktiv)"
    fi
}
zle -N safe-rm
bindkey "^Xr" safe-rm

3 Erweiterte Shortcuts / ZLE-Bindings

3.1 bindkey Syntax

bindkey [optionen] "taste" widget
OptionBeschreibung
-eEmacs-Keymap aktivieren
-vVi-Keymap aktivieren
-M keymapBinding in bestimmter Keymap
-r "taste"Binding entfernen
-s "taste" "string"Makro (String einfügen)
-lKeymaps auflisten
-LBindungen als bindkey-Befehle

3.2 Tastenkombinationen definieren

# Einzelne Taste
bindkey "^A" beginning-of-line

# Tastensequenz
bindkey "^X^F" fzf-file

# Mit Escape/Alt
bindkey "^[b" backward-word     # Alt+B
bindkey "\eb" backward-word     # Alternative Notation

# Pfeiltasten
bindkey "^[[A" history-search-backward
bindkey "^[[B" history-search-forward

# Funktionstasten
bindkey "^[OP" my-f1-widget     # F1
bindkey "^[[15~" my-f5-widget   # F5

3.3 Makros (String-Bindungen)

Mit -s wird ein String eingefügt statt ein Widget aufgerufen:

# Pipe zu less
bindkey -s "^Xl" " | less"

# Pipe zu grep
bindkey -s "^Xg" " | grep "

# Redirect Stderr
bindkey -s "^Xe" " 2>&1"

# Redirect zu /dev/null
bindkey -s "^Xn" " > /dev/null 2>&1"

# Git Status
bindkey -s "^Gs" "git status\n"

# Git Diff
bindkey -s "^Gd" "git diff\n"

# Häufige Befehle
bindkey -s "^Ll" "ll\n"
bindkey -s "^Lc" "clear\n"

tip

\n führt den Befehl direkt aus.

3.4 Keymap-spezifische Bindungen

# Nur im Vi-Insert-Modus
bindkey -M viins "jk" vi-cmd-mode

# Nur im Vi-Command-Modus
bindkey -M vicmd "H" beginning-of-line
bindkey -M vicmd "L" end-of-line

# Nur im Emacs-Modus
bindkey -M emacs "^Xe" edit-command-line

3.5 Bindung entfernen

# Binding entfernen
bindkey -r "^X^K"

# In bestimmter Keymap
bindkey -M viins -r "^A"

3.6 Alle Bindungen exportieren/importieren

# Aktuelle Bindungen als Befehle ausgeben
bindkey -L > ~/bindkeys-backup.zsh

# Wiederherstellen
source ~/bindkeys-backup.zsh

4 Erweiterte Tastenkombinationen

4.1 Emacs-Modus Referenz

Navigation:

KürzelWidgetBeschreibung
Ctrl+Abeginning-of-lineZeilenanfang
Ctrl+Eend-of-lineZeilenende
Ctrl+Fforward-charZeichen vor
Ctrl+Bbackward-charZeichen zurück
Alt+Fforward-wordWort vor
Alt+Bbackward-wordWort zurück

Bearbeitung:

KürzelWidgetBeschreibung
Ctrl+Ddelete-charZeichen löschen
Ctrl+Hbackward-delete-charBackspace
Ctrl+Wbackward-kill-wordWort rückwärts löschen
Alt+Dkill-wordWort vorwärts löschen
Ctrl+Kkill-lineBis Zeilenende löschen
Ctrl+Ubackward-kill-lineBis Zeilenanfang löschen
Ctrl+YyankEinfügen
Alt+Yyank-popVorheriges aus Kill-Ring
Ctrl+Ttranspose-charsZeichen tauschen
Alt+Ttranspose-wordsWörter tauschen
Alt+Uup-case-wordWort GROSS
Alt+Ldown-case-wordWort klein
Alt+Ccapitalize-wordWort Kapitalisieren

History:

KürzelWidgetBeschreibung
Ctrl+Pup-line-or-historyVorheriger Befehl
Ctrl+Ndown-line-or-historyNächster Befehl
Ctrl+Rhistory-incremental-search-backwardHistory suchen rückwärts
Ctrl+Shistory-incremental-search-forwardHistory suchen vorwärts
Alt+<beginning-of-buffer-or-historyErste History
Alt+>end-of-buffer-or-historyLetzte History
Alt+.insert-last-wordLetztes Argument

Sonstiges:

KürzelWidgetBeschreibung
Ctrl+Lclear-screenBildschirm löschen
Ctrl+_undoRückgängig
Ctrl+X Ctrl+UundoRückgängig (alternativ)
Ctrl+X Ctrl+Eedit-command-lineIn Editor öffnen
Tabexpand-or-completeCompletion
Ctrl+Gsend-breakAbbrechen

4.2 Vi-Modus Referenz

Modus wechseln:

KürzelBeschreibung
EscapeInsert → Command
iInsert vor Cursor
aInsert nach Cursor
AInsert am Zeilenende
IInsert am Zeilenanfang
oNeue Zeile darunter
ONeue Zeile darüber

Navigation (Command-Modus):

KürzelBeschreibung
h / lLinks / Rechts
j / kHistory runter / hoch
w / bWort vor / zurück
eWortende
0 / $Zeilenanfang / -ende
^Erstes Nicht-Whitespace
f{char}Vorwärts zu Zeichen
F{char}Rückwärts zu Zeichen
t{char}Vor Zeichen
T{char}Nach Zeichen (rückwärts)

Bearbeitung (Command-Modus):

KürzelBeschreibung
xZeichen löschen
XZeichen davor löschen
ddGanze Zeile löschen
DBis Zeilenende löschen
dwWort löschen
cwWort ändern
ccZeile ändern
CBis Zeilenende ändern
r{char}Zeichen ersetzen
RReplace-Modus
yyZeile kopieren
ywWort kopieren
p / PEinfügen nach/vor
uUndo
Ctrl+RRedo

History (Command-Modus):

KürzelBeschreibung
/patternVorwärts suchen
?patternRückwärts suchen
n / NNächster / Vorheriger Treffer

4.3 Eigene Keymap erstellen

# Neue Keymap basierend auf emacs
bindkey -N mymap emacs

# Anpassungen
bindkey -M mymap "^Xd" insert-date
bindkey -M mymap "^Xs" prepend-sudo

# Keymap aktivieren
bindkey -A mymap main

4.4 Contextuelle Bindings

# Binding nur bei bestimmter Eingabe
# Beispiel: Tab verhält sich unterschiedlich

smart-tab() {
    if [-z "$LBUFFER"](-z%20"$LBUFFER".md); then
        # Leere Zeile: 4 Spaces einfügen
        LBUFFER+="    "
    elif ["$LBUFFER" =~ '^ +$']("$LBUFFER"%20=~%20'^%20+$'.md); then
        # Nur Whitespace: weitere Spaces
        LBUFFER+="    "
    else
        # Sonst: normale Completion
        zle expand-or-complete
    fi
}
zle -N smart-tab
bindkey "^I" smart-tab

5 Beispiele & Use-Cases

5.1 Produktivitäts-Widgets

Befehl in Editor öffnen:

# Bereits eingebaut, aber oft nicht gebunden
autoload -U edit-command-line
zle -N edit-command-line
bindkey "^Xe" edit-command-line
bindkey "^X^E" edit-command-line

Git-Integration:

# Git Branch wechseln mit fzf
fzf-git-branch() {
    local branch
    branch=$(git branch --all | grep -v HEAD | fzf --preview 'git log --oneline -20 {1}')
    branch=${branch#remotes/origin/}
    branch=${branch## }
    if [-n "$branch"](-n%20"$branch".md); then
        BUFFER="git checkout $branch"
        zle accept-line
    fi
}
zle -N fzf-git-branch
bindkey "^Gb" fzf-git-branch

# Git Status anzeigen (ohne Ausführung)
show-git-status() {
    zle -M "$(git status -s 2>/dev/null || echo 'Not a git repo')"
}
zle -N show-git-status
bindkey "^Gs" show-git-status

# Git Add mit fzf
fzf-git-add() {
    local files
    files=$(git status -s | fzf -m | awk '{print $2}')
    if [-n "$files"](-n%20"$files".md); then
        BUFFER="git add ${files//$'\n'/ }"
        CURSOR=$#BUFFER
    fi
    zle redisplay
}
zle -N fzf-git-add
bindkey "^Ga" fzf-git-add

Verzeichnis-Navigation:

# Schnell zu häufigen Verzeichnissen
goto-projects() {
    local dir
    dir=$(find ~/projects -maxdepth 1 -type d | fzf)
    if [-n "$dir"](-n%20"$dir".md); then
        cd "$dir"
        zle reset-prompt
    fi
}
zle -N goto-projects
bindkey "^Xp" goto-projects

# Parent Directory
goto-parent() {
    cd ..
    zle reset-prompt
}
zle -N goto-parent
bindkey "^X." goto-parent

# Zurück zum vorherigen Verzeichnis
goto-previous() {
    cd - > /dev/null
    zle reset-prompt
}
zle -N goto-previous
bindkey "^X-" goto-previous

5.2 Textmanipulation

Pipe-Operatoren schnell einfügen:

# Pipe-Menü
pipe-menu() {
    local pipes=(
        "| less"
        "| grep "
        "| head -20"
        "| tail -20"
        "| wc -l"
        "| sort"
        "| sort -u"
        "| xargs "
        "| awk '{print \$1}'"
        "| sed 's///g'"
        "> /dev/null 2>&1"
        "2>&1"
    )
    local selected
    selected=$(printf '%s\n' "${pipes[@]}" | fzf --prompt="Pipe: ")
    if [-n "$selected"](-n%20"$selected".md); then
        LBUFFER+=" $selected"
    fi
    zle redisplay
}
zle -N pipe-menu
bindkey "^X|" pipe-menu

Pfad expandieren:

# ~ und Variablen expandieren
expand-path() {
    BUFFER="${(e)BUFFER}"
}
zle -N expand-path
bindkey "^Xx" expand-path

# Aktuelles Wort zu absolutem Pfad
expand-to-absolute() {
    local word="${LBUFFER##* }"
    local rest="${LBUFFER% *}"
    local abs="${word:A}"
    if ["$rest" == "$LBUFFER"]("$rest"%20==%20"$LBUFFER".md); then
        LBUFFER="$abs"
    else
        LBUFFER="$rest $abs"
    fi
}
zle -N expand-to-absolute
bindkey "^Xa" expand-to-absolute

Wort-Operationen:

# Wort duplizieren
duplicate-word() {
    local word="${LBUFFER##* }"
    LBUFFER+=" $word"
}
zle -N duplicate-word
bindkey "^Xw" duplicate-word

# Wörter rückwärts
reverse-words() {
    local words=(${=BUFFER})
    BUFFER="${(j: :)${(Oa)words}}"
}
zle -N reverse-words

5.3 History-Erweiterungen

History mit Preview:

fzf-history-widget() {
    local selected
    selected=$(fc -rl 1 | fzf +s --tac \
        --preview 'echo {}' \
        --preview-window down:3:wrap)
    if [-n "$selected"](-n%20"$selected".md); then
        # Nummer entfernen
        BUFFER="${selected#*  }"
        CURSOR=$#BUFFER
    fi
    zle redisplay
}
zle -N fzf-history-widget
bindkey "^R" fzf-history-widget

Letzten Befehl modifizieren:

# Letzten Befehl holen und editierbar machen
recall-last-command() {
    BUFFER=$(fc -ln -1)
    CURSOR=$#BUFFER
}
zle -N recall-last-command
bindkey "^[r" recall-last-command

5.4 Prompt-Integration

# Prompt neu zeichnen nach Widget
update-prompt() {
    # Prompt-relevante Variablen aktualisieren
    zle reset-prompt
}

# Nach cd immer Prompt aktualisieren
cd-and-update() {
    if [-n "$BUFFER"](-n%20"$BUFFER".md); then
        zle accept-line
        zle reset-prompt
    fi
}

5.5 Debugging-Widgets

# Aktuellen Buffer anzeigen
debug-buffer() {
    zle -M "BUFFER: '$BUFFER' | LBUFFER: '$LBUFFER' | CURSOR: $CURSOR"
}
zle -N debug-buffer
bindkey "^Xb" debug-buffer

# Letzte Tasten anzeigen
debug-keys() {
    zle -M "KEYS: '$KEYS' | WIDGET: '$WIDGET' | LASTWIDGET: '$LASTWIDGET'"
}
zle -N debug-keys
bindkey "^Xk" debug-keys

# Widget-Liste durchsuchen
search-widgets() {
    local widget
    widget=$(zle -la | fzf --prompt="Widget: ")
    if [-n "$widget"](-n%20"$widget".md); then
        zle -M "Widget: $widget"
    fi
}
zle -N search-widgets
bindkey "^X?" search-widgets

5.6 Vollständige .zshrc-Integration

# ~/.zshrc - ZLE-Konfiguration

# === Grundeinstellungen ===
bindkey -e                              # Emacs-Modus

# === Standard-Verbesserungen ===
bindkey "^[[A" history-search-backward  # Pfeil hoch: History mit Prefix
bindkey "^[[B" history-search-forward   # Pfeil runter: History mit Prefix
bindkey "^[[H" beginning-of-line        # Home
bindkey "^[[F" end-of-line              # End
bindkey "^[[3~" delete-char             # Delete

# === Editor ===
autoload -U edit-command-line
zle -N edit-command-line
bindkey "^Xe" edit-command-line
bindkey "^X^E" edit-command-line

# === Eigene Widgets ===

# Sudo voranstellen
prepend-sudo() {
    [$BUFFER != sudo\ *]($BUFFER%20!=%20sudo\%20*.md) && BUFFER="sudo $BUFFER" && ((CURSOR+=5))
}
zle -N prepend-sudo
bindkey "^[s" prepend-sudo

# Datum einfügen
insert-date() { LBUFFER+=$(date +%Y-%m-%d); }
zle -N insert-date
bindkey "^Xd" insert-date

# Clipboard (macOS)
copy-buffer() { echo -n "$BUFFER" | pbcopy; zle -M "Copied"; }
paste-buffer() { LBUFFER+=$(pbpaste); }
zle -N copy-buffer
zle -N paste-buffer
bindkey "^Xy" copy-buffer
bindkey "^Xp" paste-buffer

# === Makros ===
bindkey -s "^Xl" " | less"
bindkey -s "^Xg" " | grep "
bindkey -s "^Xn" " > /dev/null 2>&1"

# === fzf-Integration (falls installiert) ===
if command -v fzf &> /dev/null; then
    # History
    fzf-history() {
        local cmd=$(fc -ln 1 | fzf --tac --no-sort)
        [-n "$cmd"](-n%20"$cmd".md) && BUFFER="$cmd" && CURSOR=$#BUFFER
        zle redisplay
    }
    zle -N fzf-history
    bindkey "^R" fzf-history

    # Dateien
    fzf-file() {
        local file=$(fzf)
        [-n "$file"](-n%20"$file".md) && LBUFFER+="${(q)file}"
        zle redisplay
    }
    zle -N fzf-file
    bindkey "^Xf" fzf-file

    # Verzeichnisse
    fzf-cd() {
        local dir=$(find . -type d 2>/dev/null | fzf)
        [-n "$dir"](-n%20"$dir".md) && cd "$dir" && zle reset-prompt
    }
    zle -N fzf-cd
    bindkey "^Xc" fzf-cd
fi

5.7 Cheatsheet

Widget erstellen:

widget-name() { ... }           # Funktion definieren
zle -N widget-name              # Als Widget registrieren
bindkey "^Xk" widget-name       # An Taste binden

Wichtige Variablen:

$BUFFER                         # Gesamte Zeile
$LBUFFER                        # Links vom Cursor
$RBUFFER                        # Rechts vom Cursor
$CURSOR                         # Cursor-Position

Bindkey:

bindkey                         # Alle Bindungen
bindkey "^X"                    # Was macht Ctrl+X?
bindkey "^Xk" widget            # Widget binden
bindkey -s "^Xk" "string"       # Makro binden
bindkey -r "^Xk"                # Binding entfernen
bindkey -M viins "jk" vi-cmd    # Keymap-spezifisch

Escape-Sequenzen:

^X   = Ctrl+X
^[   = Escape / Alt
^[[A = Pfeil hoch
^[[B = Pfeil runter
\n   = Enter (für Makros)

Dateisystem & Volumes

Dieses Kapitel behandelt fortgeschrittene Dateisystem-Operationen unter macOS: symbolische und harte Links, Datei-Metadaten, Volume-Management, Disk Images und Backup-Werkzeuge.

Links ermöglichen es, auf Dateien oder Verzeichnisse an mehreren Stellen im Dateisystem zu verweisen, ohne die Daten zu duplizieren.

Ein symbolischer Link (Symlink, Softlink) ist ein Verweis auf einen Pfad. Er funktioniert wie eine Verknüpfung und kann auf Dateien oder Verzeichnisse zeigen – auch über Dateisystemgrenzen hinweg.

Syntax:

ln -s ZIEL LINKNAME

Beispiele:

# Symlink auf eine Datei
ln -s ~/Documents/config.yaml ~/.config/app/config.yaml

# Symlink auf ein Verzeichnis
ln -s ~/Dropbox/Projekte ~/Projekte

# Symlink mit relativem Pfad
cd ~/bin
ln -s ../scripts/backup.sh backup

# Symlink in anderem Verzeichnis erstellen
ln -s /opt/homebrew/bin/python3 /usr/local/bin/python

Eigenschaften:

MerkmalSymbolischer Link
Verweist aufPfad (String)
Ziel gelöschtLink wird “broken” (toter Link)
Über Dateisysteme✅ Ja
Auf Verzeichnisse✅ Ja
Eigene Inode✅ Ja (eigene Datei)
Erkennungls -l zeigt l und ->

Symlink erkennen:

ls -l ~/Projekte
# lrwxr-xr-x  1 max  staff  20 Jan 15 10:30 Projekte -> Dropbox/Projekte

# Nur Symlinks anzeigen
ls -la | grep "^l"

# Mit file-Befehl
file ~/Projekte
# /Users/max/Projekte: symbolic link to Dropbox/Projekte

Ein harter Link ist ein zusätzlicher Verzeichniseintrag für dieselbe Datei (Inode). Die Datei existiert erst dann nicht mehr, wenn alle harten Links gelöscht wurden.

Syntax:

ln ZIEL LINKNAME

Beispiele:

# Harter Link auf Datei
ln original.txt hardlink.txt

# Beide zeigen auf dieselbe Inode
ls -li original.txt hardlink.txt
# 12345678 -rw-r--r--  2 max  staff  100 Jan 15 10:30 hardlink.txt
# 12345678 -rw-r--r--  2 max  staff  100 Jan 15 10:30 original.txt
#    ^-- gleiche Inode-Nummer        ^-- Link-Count = 2

Eigenschaften:

MerkmalHarter Link
Verweist aufInode (Daten direkt)
Ziel gelöschtLink funktioniert weiter
Über Dateisysteme❌ Nein
Auf Verzeichnisse❌ Nein (außer . und ..)
Eigene Inode❌ Nein (teilt Inode)
ErkennungLink-Count > 1

Einschränkungen unter macOS:

# Harte Links auf Verzeichnisse sind nicht erlaubt
ln /Users/max/Documents /Users/max/Docs
# ln: /Users/max/Documents: Is a directory

# Harte Links über Volumes hinweg nicht möglich
ln /Volumes/USB/file.txt ~/file.txt
# ln: /Volumes/USB/file.txt: Cross-device link

1.3 Unterschiede und Anwendungsfälle

Vergleichstabelle:

MerkmalSymbolischer LinkHarter Link
Verweist aufPfadnameInode
Eigene InodeJaNein
Funktioniert nach Löschen des OriginalsNein (broken)Ja
Über DateisystemeJaNein
Auf VerzeichnisseJaNein
PlatzbedarfMinimal (Pfad)Keiner (Eintrag)
Typische NutzungVerknüpfungen, AliaseBackups, Deduplizierung

Wann Symlinks verwenden:

  • Verknüpfungen zu Verzeichnissen
  • Links über verschiedene Volumes/Partitionen
  • Wenn der Link als “Verweis” erkennbar sein soll
  • Konfigurationsdateien an mehreren Orten verfügbar machen
  • Versionierte Verzeichnisse (z.B. current -> v2.1.0)

Wann harte Links verwenden:

  • Backup-Systeme (Time Machine nutzt harte Links)
  • Deduplizierung von Dateien
  • Wenn die Datei auch nach Löschen des “Originals” erhalten bleiben soll
  • Space-effiziente Kopien

Beispiel: Versionierung mit Symlinks:

# Versionen als Verzeichnisse
mkdir -p ~/apps/myapp-1.0.0 ~/apps/myapp-1.1.0 ~/apps/myapp-2.0.0

# Symlink auf aktuelle Version
ln -s ~/apps/myapp-2.0.0 ~/apps/myapp-current

# Update auf neue Version
rm ~/apps/myapp-current
ln -s ~/apps/myapp-2.1.0 ~/apps/myapp-current

# Oder mit ln -sf (force)
ln -sf ~/apps/myapp-2.1.0 ~/apps/myapp-current

readlink – Linkziel auslesen:

# Symlink-Ziel anzeigen
readlink ~/Projekte
# Dropbox/Projekte

# Kanonischen (absoluten) Pfad auflösen
readlink -f ~/Projekte
# /Users/max/Dropbox/Projekte

# Auch für nicht existierende Pfade
readlink -f ./relative/path/../file.txt
# /Users/max/current/file.txt

Optionen:

OptionBeschreibung
-fKanonischer Pfad (alle Symlinks auflösen)
-nKeine Newline am Ende

unlink – Link entfernen:

# Symlink entfernen (nicht das Ziel!)
unlink ~/Projekte

# Äquivalent zu rm für einzelne Dateien
rm ~/Projekte

caution

Bei Verzeichnis-Symlinks keinen Trailing Slash verwenden!

# RICHTIG: Entfernt den Symlink
rm ~/Projekte

# FALSCH: Versucht Inhalt des Zielverzeichnisses zu löschen!
rm -r ~/Projekte/

Der Trailing Slash (/) bewirkt, dass die Shell den Symlink auflöst und dem Befehl das Zielverzeichnis übergibt. Mit rm -r ~/Projekte/ löscht man daher nicht den Symlink, sondern den Inhalt des verlinkten Verzeichnisses – ein häufiger und folgenschwerer Fehler.

Links finden:

# Alle Symlinks in einem Verzeichnis
find ~/Documents -type l

# Broken Symlinks finden
find ~/Documents -type l ! -exec test -e {} \; -print

# Alle harten Links zu einer Datei finden (gleiche Inode)
find / -inum $(stat -f %i datei.txt) 2>/dev/null

Link-Informationen mit stat:

# Inode und Link-Count anzeigen
stat -f "Inode: %i, Links: %l" datei.txt

# Bei Symlinks: Link vs. Ziel
stat -f "Link: %N, Ziel: %Y" ~/Projekte

2 Dateisystem-Tools

2.1 stat – Detaillierte Datei-Informationen

stat zeigt umfassende Metadaten einer Datei an.

Syntax:

stat [Optionen] DATEI

Standardausgabe:

stat ~/.zshrc
# 16777220 8453123 -rw-r--r-- 1 max staff 0 2048 "Jan 15 10:30:00 2024" ...

Formatierte Ausgabe (macOS BSD-stat):

# Lesbare Ausgabe
stat -x ~/.zshrc

# Bestimmte Felder
stat -f "%N: %z Bytes, modified %Sm" ~/.zshrc
# .zshrc: 2048 Bytes, modified Jan 15 10:30:00 2024

# Nur Dateigröße
stat -f %z ~/.zshrc
# 2048

# Nur Inode
stat -f %i ~/.zshrc
# 8453123

# Berechtigungen oktal
stat -f %Op ~/.zshrc
# 100644

# Besitzer und Gruppe
stat -f "%Su:%Sg" ~/.zshrc
# max:staff

Format-Spezifizierer (BSD/macOS):

SpezifiziererBeschreibung
%NDateiname
%zGröße in Bytes
%iInode-Nummer
%lAnzahl harter Links
%OpBerechtigungen (oktal)
%SpBerechtigungen (symbolisch)
%SuBesitzer (Name)
%SgGruppe (Name)
%uBesitzer (UID)
%gGruppe (GID)
%SaZugriffszeit
%SmÄnderungszeit
%ScStatusänderungszeit
%SBErstellungszeit (macOS)
%YSymlink-Ziel
%TDateityp

Beispiele:

# Erstellungsdatum (macOS-spezifisch)
stat -f "Erstellt: %SB" -t "%Y-%m-%d %H:%M" ~/.zshrc

# Vergleich zweier Dateien
for f in file1.txt file2.txt; do
    stat -f "%N: %z Bytes, %Sm" "$f"
done

# Alle Infos für Skript
stat -f "size=%z;inode=%i;mode=%Op;uid=%u;gid=%g" datei.txt

2.2 file – Dateityp erkennen

file analysiert den Inhalt einer Datei und bestimmt ihren Typ – unabhängig von der Dateiendung.

Syntax:

file [Optionen] DATEI...

Beispiele:

file document.pdf
# document.pdf: PDF document, version 1.7

file image.png
# image.png: PNG image data, 1920 x 1080, 8-bit/color RGBA

file script.sh
# script.sh: Bourne-Again shell script, ASCII text executable

file binary
# binary: Mach-O 64-bit executable arm64

file archive.tar.gz
# archive.tar.gz: gzip compressed data

file unknown
# unknown: ASCII text, with very long lines

Optionen:

OptionBeschreibung
-bBrief: Kein Dateiname in Ausgabe
-iMIME-Typ ausgeben
-IMIME-Typ mit Encoding
-LSymlinks folgen
-zKomprimierte Dateien untersuchen

Anwendungen:

# MIME-Typ für Webserver
file -I document.pdf
# document.pdf: application/pdf; charset=binary

# Mehrere Dateien
file *
# bild.jpg:    JPEG image data
# script.py:   Python script, ASCII text
# data.json:   JSON data

# In Skripten: Typ prüfen
if file -b "$1" | grep -q "shell script"; then
    echo "Ist ein Shell-Skript"
fi

# Nur echte Bilder finden
find . -type f -exec file {} \; | grep "image data"

2.3 mdls – macOS-Metadaten anzeigen

mdls zeigt Spotlight-Metadaten einer Datei an. Diese umfassen weit mehr als Standard-Dateiattribute.

Syntax:

mdls [Optionen] DATEI

Beispiel:

mdls foto.jpg
# kMDItemContentType         = "public.jpeg"
# kMDItemContentTypeTree     = (
#     "public.jpeg",
#     "public.image",
#     "public.data"
# )
# kMDItemDateTimeOriginal    = 2024-01-15 10:30:00 +0000
# kMDItemPixelHeight         = 1080
# kMDItemPixelWidth          = 1920
# kMDItemColorSpace          = "RGB"
# ...

Bestimmte Attribute abfragen:

# Nur bestimmtes Attribut
mdls -name kMDItemContentType foto.jpg
# kMDItemContentType = "public.jpeg"

# Mehrere Attribute
mdls -name kMDItemPixelWidth -name kMDItemPixelHeight foto.jpg

# Wert ohne Attributnamen
mdls -raw -name kMDItemPixelWidth foto.jpg
# 1920

Wichtige Metadaten-Attribute:

AttributBeschreibung
kMDItemContentTypeUTI-Dateityp
kMDItemKindLesbare Typbeschreibung
kMDItemFSNameDateiname
kMDItemFSSizeDateigröße
kMDItemFSCreationDateErstellungsdatum
kMDItemContentCreationDateInhalt erstellt
kMDItemContentModificationDateInhalt geändert
kMDItemPixelWidth/HeightBildgröße
kMDItemDurationSecondsMedien-Dauer
kMDItemTitleTitel
kMDItemAuthorsAutoren
kMDItemWhereFromsDownload-URL

Praktische Beispiele:

# Bildabmessungen
mdls -name kMDItemPixelWidth -name kMDItemPixelHeight *.jpg

# Woher wurde Datei heruntergeladen?
mdls -name kMDItemWhereFroms ~/Downloads/installer.dmg

# Dauer eines Videos
mdls -raw -name kMDItemDurationSeconds video.mp4

# Alle PDFs mit Seitenzahl
for f in *.pdf; do
    pages=$(mdls -raw -name kMDItemNumberOfPages "$f" 2>/dev/null)
    echo "$f: $pages Seiten"
done

Metadaten setzen mit xattr:

# Extended Attributes anzeigen
xattr -l datei.txt

# Quarantäne-Flag entfernen (heruntergeladene Dateien)
xattr -d com.apple.quarantine datei.app

# Alle xattrs entfernen
xattr -c datei.txt

3 Volumes & Mounten

3.1 diskutil – Laufwerke verwalten

diskutil ist das zentrale macOS-Tool zur Verwaltung von Festplatten, Partitionen und Volumes.

Laufwerke auflisten:

# Alle Laufwerke
diskutil list

# Ausgabe:
# /dev/disk0 (internal, physical):
#    #:                       TYPE NAME                    SIZE
#    0:      GUID_partition_scheme                        *500.1 GB
#    1:                        EFI EFI                     209.7 MB
#    2:                 Apple_APFS Container disk1         499.9 GB
#
# /dev/disk1 (synthesized):
#    #:                       TYPE NAME                    SIZE
#    0:      APFS Container Scheme -                      +499.9 GB
#    1:                APFS Volume Macintosh HD            12.4 GB
#    2:                APFS Volume Preboot                 23.1 MB
#    ...

Volume-Informationen:

# Detaillierte Infos zu einem Volume
diskutil info /dev/disk1s1

# Infos über Mount-Point
diskutil info /Volumes/USB

# Nur bestimmte Info
diskutil info /dev/disk1s1 | grep "Volume Name"

Volumes ein- und aushängen:

# Volume aushängen
diskutil unmount /Volumes/USB
diskutil unmount /dev/disk2s1

# Volume einhängen
diskutil mount /dev/disk2s1

# Alle Volumes eines Laufwerks aushängen
diskutil unmountDisk /dev/disk2

# Laufwerk sicher auswerfen
diskutil eject /dev/disk2

Partitionen und Formatierung:

# Volume löschen (APFS)
diskutil eraseVolume APFS "NeuesVolume" /dev/disk2s1

# Ganzes Laufwerk formatieren (ACHTUNG!)
diskutil eraseDisk APFS "MeinDisk" /dev/disk2

# Verfügbare Dateisystem-Typen
diskutil listFilesystems

# Partition hinzufügen (APFS Container)
diskutil apfs addVolume disk1 APFS "Daten"

APFS-spezifische Befehle:

# APFS Container auflisten
diskutil apfs list

# Volume zu Container hinzufügen
diskutil apfs addVolume disk1 APFS "NeuesVolume"

# Volume löschen
diskutil apfs deleteVolume disk1s3

# Volume umbenennen
diskutil rename disk1s2 "Neuer Name"

Festplatten reparieren:

# Dateisystem prüfen
diskutil verifyVolume /dev/disk1s1

# Dateisystem reparieren
diskutil repairVolume /dev/disk1s1

# First Aid (GUI-Äquivalent)
diskutil verifyDisk /dev/disk0
diskutil repairDisk /dev/disk0

3.2 mount / umount – Volumes ein-/aushängen

Die klassischen Unix-Befehle mount und umount funktionieren auch unter macOS.

Aktuelle Mounts anzeigen:

# Alle gemounteten Volumes
mount

# Nur bestimmter Typ
mount | grep apfs
mount | grep nfs

# Übersichtlicher mit df
df -h

Volume manuell mounten:

# Mount-Point erstellen
sudo mkdir /Volumes/MeinUSB

# Volume mounten
sudo mount -t msdos /dev/disk2s1 /Volumes/MeinUSB

# Mit Optionen
sudo mount -t hfs -o ro /dev/disk2s1 /Volumes/ReadOnly

Volume aushängen:

# Normal aushängen
sudo umount /Volumes/MeinUSB

# Erzwingen (wenn "busy")
sudo umount -f /Volumes/MeinUSB

# Besser: diskutil verwenden
diskutil unmount /Volumes/MeinUSB

Mount-Optionen:

OptionBeschreibung
-t TYPEDateisystem-Typ (apfs, hfs, msdos, ntfs, nfs, smbfs)
-o roRead-only
-o rwRead-write
-o noexecKeine Ausführung erlauben
-o nosuidSetuid ignorieren

3.3 Netzlaufwerke mounten (SMB, NFS, AFP)

SMB/CIFS (Windows-Freigaben):

# Mit mount_smbfs
mount_smbfs //user@server/share /Volumes/Share

# Mit Passwort (unsicher, besser Keychain nutzen)
mount_smbfs //user:passwort@server/share /Volumes/Share

# Über Finder-URL (öffnet Finder)
open smb://server/share

# Mount-Point vorbereiten
mkdir -p /Volumes/NAS
mount_smbfs //max@nas.local/Daten /Volumes/NAS

NFS:

# NFS-Mount
sudo mount -t nfs server:/export/share /Volumes/NFS

# Mit Optionen
sudo mount -t nfs -o resvport,rw server:/share /Volumes/NFS

# NFS-Exports auf Server anzeigen
showmount -e server

AFP (Apple Filing Protocol, veraltet):

# AFP-Mount
mount_afp afp://user@server/share /Volumes/AFP

# Über Finder
open afp://server/share

WebDAV:

# WebDAV mounten
mount_webdav https://server/dav /Volumes/WebDAV

Netzlaufwerk per Skript verbinden:

#!/bin/zsh
# mount-nas.sh

SERVER="nas.local"
SHARE="Daten"
USER="max"
MOUNTPOINT="/Volumes/NAS"

# Mount-Point erstellen falls nötig
[-d "$MOUNTPOINT"](-d%20"$MOUNTPOINT".md) || mkdir -p "$MOUNTPOINT"

# Prüfen ob bereits gemountet
if mount | grep -q "$MOUNTPOINT"; then
    echo "Bereits verbunden"
    exit 0
fi

# Mounten (Passwort aus Keychain)
mount_smbfs "//${USER}@${SERVER}/${SHARE}" "$MOUNTPOINT"

if [$? -eq 0]($?%20-eq%200.md); then
    echo "Erfolgreich verbunden: $MOUNTPOINT"
else
    echo "Fehler beim Verbinden"
    exit 1
fi

3.4 /etc/fstab – Automatisches Mounten

Die Datei /etc/fstab definiert Volumes, die beim Systemstart automatisch gemountet werden.

Syntax:

DEVICE  MOUNTPOINT  FSTYPE  OPTIONS  DUMP  PASS

Beispiele:

# /etc/fstab bearbeiten
sudo nano /etc/fstab
# NFS-Share beim Start mounten
server:/export/data  /Volumes/Data  nfs  rw,resvport  0  0

# SMB-Share (mit Benutzername)
//user@server/share  /Volumes/Share  smbfs  -N  0  0

# USB-Laufwerk nach UUID
UUID=1234-5678  /Volumes/USB  msdos  rw  0  0

# Volume read-only mounten
LABEL=Backup  /Volumes/Backup  apfs  ro  0  0

UUID ermitteln:

diskutil info /dev/disk2s1 | grep "Volume UUID"

fstab neu einlesen:

sudo automount -vc

note

macOS verwendet primär /etc/auto_master für Automounts. Für Netzlaufwerke ist oft ein Login-Item oder launchd-Job praktischer.

Automatisches Mounten mit launchd:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.user.mount-nas</string>

    <key>ProgramArguments</key>
    <array>
        <string>/usr/bin/mount_smbfs</string>
        <string>//user@server/share</string>
        <string>/Volumes/NAS</string>
    </array>

    <key>RunAtLoad</key>
    <true/>

    <key>WatchPaths</key>
    <array>
        <string>/etc/resolv.conf</string>
    </array>
</dict>
</plist>

4 Disk Images & Sparse Files

Disk Images sind Container-Dateien, die ein virtuelles Dateisystem enthalten. Unter macOS sind sie allgegenwärtig für Software-Verteilung, verschlüsselte Container und Backups.

Typen von Disk Images:

TypEndungBeschreibung
Read-only.dmgKomprimiert, nicht veränderbar
Read/write.dmgVeränderbar, feste Größe
Sparse.sparseimageWächst bei Bedarf
Sparse Bundle.sparsebundleWächst, besteht aus Bändern
DVD/CD Master.cdrFür Brennen

Sparse Files:

Ein Sparse File belegt nur so viel Platz wie tatsächlich geschriebene Daten:

# Sparse File erstellen (10GB logisch, 0 Bytes real)
dd if=/dev/zero of=sparse.img bs=1 count=0 seek=10G

# Tatsächliche vs. logische Größe
ls -lh sparse.img    # zeigt 10G
du -h sparse.img     # zeigt 0B (oder wenige KB)

# Mit stat
stat -f "Logisch: %z, Blöcke: %b" sparse.img

5 Erweiterte Datei- und Backupwerkzeuge

In diesem Kapitel geht es um Befehle, mit denen man ganze Verzeichnisse oder Datenbestände effizient kopieren, klonen oder synchronisieren kannst.

Während einfache Kopierbefehle wie cp für einzelne Dateien ausreichen, sind ditto und rsync leistungsfähige Werkzeuge für komplexere Aufgaben, wie beispielsweise die Erstellung von Backups, der Übertragung großer Datenmengen oder beim der Spiegelung von Ordnern inklusive Berechtigungen, Metadaten und versteckten Dateien.

5.1 cp – Der klassische Kopierbefehl

cp ist der einfachste Befehl zum Kopieren von Dateien oder Verzeichnissen. Mit der Option -R (rekursiv) kopiert er auch Unterordner.

Beispiel:

cp -R Quelle Ziel

Verwendung:

Für schnelle, einfache Kopiervorgänge, etwa einzelne Dateien oder kleine Ordner.

Vorteile:

  • immer verfügbar, sehr einfach
  • Schnell bei wenigen Dateien

Nachteile:

  • Keine Fortschrittsanzeige
  • Keine Synchronisation
  • Kopiert oft keine versteckten Metadaten
  • Keine Option zum Erstellen von Archiven

5.2 ditto – Systemgerechtes Kopieren und Archivieren

Mit dem Befehl ditto werden Verzeichnisse kopiert/dupliziert.

note

Im Gegensatz zu cp kann ditto Ressourcendateien, Metadaten, Rechte, unsichtbare Dateien und Paketstrukturen (z. B. App-Bundles) korrekt handhaben.

Syntax:

ditto [Optionen] Quelle Ziel

Verwendung:

Ideal zum Klonen von Projekten, App-Bundles oder Backups auf demselben System. Auch nützlich zum Erstellen von ZIP-Archiven mit -c -k.

Vorteile:

  • Bewahrt Metadaten, ACLs, Ressourcen
  • Zusammenführen statt stumpfem Überschreiben
  • Kann ZIP-Archive erstellen
  • macOS-optimiert

Nachteile:

  • Arbeitet nur lokal (kein Netzwerktransfer)
  • Weniger effizient bei großen oder häufigen Synchronisationen

5.2.1 Beispiele

Ordner vollständig kopieren (inklusive versteckter Dateien wie .DS_Store und .git):

ditto ~/Projekte/ProjektA ~/Backup/ProjektA

Eine App korrekt klonen (inkl. Metadaten):

sudo ditto /Applications/App.app /Applications/App-Kopie.app

Nur neue oder geänderte Dateien kopieren:

ditto --update ~/Projekte/ ~/Backup/Projekte/

Eine ZIP-Datei aus einem Ordner erzeugen:

ditto -c -k --sequesterRsrc --keepParent "MeinOrdner" "MeinOrdner.zip"

→ Erstellt ein ZIP-Archiv, das auch Ressourcendateien und Metadaten beibehält.

5.2.2 Häufige Optionen

OptionBedeutung
--updateNur neuere Dateien kopieren
--norsrcRessourcen-Forks nicht mitkopieren
--rsrcRessourcen-Forks mitkopieren (Standardverhalten)
-VZeigt detaillierte Ausgaben (Verbose-Modus)
-XBewahrt erweiterte Attribute (ACLs, Metadaten)
-c -kErstellt ein ZIP-Archiv
--sequesterRsrc --keepParentErhält Ressourcen und übergeordnete Ordnerstruktur beim Archivieren

5.2.3 Typische Anwendungsfälle

  • Vollständige Kopien von App-Bundles, Projekten oder Verzeichnissen unter Beibehaltung macOS-spezifischer Dateiattribute (z. B. ACLs, HFS+ Metadaten)
  • Erstellung von Backups und ZIP-Archiven
  • Zusammenführen von Ordnern mit Erhalt von Metadaten
  • Alternative zu cp -R mit besserer macOS-Kompatibilität
  • Installer-Skripte und Backup-Tools

5.3 rsync – Effiziente Synchronisation und Spiegelung

Der Befehl rsync wird verwendet, um Dateien und Verzeichnisse effizient zu synchronisieren – lokal oder über ein Netzwerk. Er überträgt nur geänderte Teile von Dateien, was ihn besonders schnell und bandbreitenschonend macht. rsync ist ideal für Backups, Spiegelungen und regelmäßige Synchronisationen zwischen Verzeichnissen oder Rechnern.

Syntax:

rsync [Optionen] Quelle Ziel

Verwendung:

Für effiziente Backups, Netzwerkübertragungen und automatisierte Synchronisationen.

Vorteile:

  • Überträgt nur geänderte Dateien → sehr schnell bei Wiederholungen
  • Netzwerkfähig (via SSH)
  • Fortschrittsanzeige, Testlauf-Modus (-n)
  • Ideal für Skripte und Automatisierung

Nachteile:

  • Etwas komplexer in der Handhabung
  • Irrtümlich gesetzte Optionen (z. B. --delete) können Daten löschen
  • Nicht alle macOS-spezifischen Metadaten werden immer exakt kopiert

5.3.1 Beispiele

Einen Ordner lokal synchronisieren:

rsync -av ~/Projekte/MeinApp/ ~/Backup/MeinApp/

→ Kopiert alle Dateien und Unterordner, behält Berechtigungen und Zeiten bei, synchronisiert nur geänderte Dateien.

Dateien auf einen anderen Rechner übertragen:

rsync -avz ~/Projekte/MeinApp/ user@server:/Users/user/Backup/MeinApp/

→ Überträgt den Ordner über SSH auf einen entfernten Rechner. Die Option -z komprimiert die Daten während der Übertragung.

Nur geänderte Dateien aktualisieren:

rsync -avu ~/Projekte/MeinApp/ ~/Backup/MeinApp/

→ Die Option -u sorgt dafür, dass nur neuere Dateien kopiert werden.

Dateien löschen, die im Ziel nicht mehr existieren:

rsync -av --delete ~/Projekte/MeinApp/ ~/Backup/MeinApp/

→ Hält das Zielverzeichnis exakt synchron mit der Quelle (Achtung: löscht zusätzliche Dateien im Ziel!).

5.3.2 Häufige Optionen

OptionBeschreibung
-n“Testlauf” – zeigt, was passieren würde, ohne Änderungen vorzunehmen
-aArchivmodus: kopiert rekursiv und bewahrt Berechtigungen, Zeiten und Symlinks
-vVerbose: zeigt detaillierte Fortschrittsinformationen
-zKomprimiert Daten während der Übertragung
-uÜberspringt Dateien, die im Ziel neuer sind
--deleteLöscht Dateien im Ziel, die in der Quelle nicht mehr existieren
--progressZeigt den Fortschritt jeder Dateiübertragung
-e sshErzwingt die Verwendung von SSH (meist automatisch aktiv)

5.3.3 Typische Anwendungsfälle

  • Erstellung und Aktualisierung von Backups´
  • Synchronisation zwischen zwei Festplatten oder Rechnern
  • Spiegelung von Verzeichnisstrukturen auf Servern
  • Automatisierte Sicherungsskripte (z. B. via cron)
  • Übertragung großer Datenmengen mit minimaler Redundanz

5.4 Vergleich: cp, ditto und rsync

Es gibt mehrere Befehle, um Dateien und Ordner zu kopieren oder zu sichern. Die drei wichtigsten sind cp, ditto und rsync. Obwohl sie ähnliche Aufgaben erfüllen, unterscheiden sie sich deutlich in Funktionsumfang, Zuverlässigkeit und Einsatzgebiet.

Merkmalcpdittorsync
Kopiert Verzeichnisse rekursiv✅ (-R)
Bewahrt Metadaten / ACLs⚠️ Teilweise✅ Vollständig⚠️ Teilweise
Versteckte Dateien (.git, .DS_Store)❌ Nicht immer
Nur geänderte Dateien kopieren
Fortschrittsanzeige⚠️ (mit -V)✅ (--progress)
Zusammenführen statt Überschreiben
Erstellung von Archiven (ZIP etc.)✅ (-c -k)
Netzwerkfähig✅ (SSH)
Skript-/Backup-tauglich⚠️ Einfach✅ Lokal✅ Sehr gut
Komplexität⭐ Einfach⭐⭐ Mittel⭐⭐⭐ Anspruchsvoll
  • cp: Für einfache, schnelle Kopien – ideal bei Einzeldaten.
  • ditto: Für vollständige, macOS-gerechte Kopien und Archive.
  • rsync: Für effiziente, wiederholbare Backups und Synchronisationen – auch über Netzwerke.

5.5 hdiutil – Disk Images erstellen und verwalten

hdiutil ist das mächtige Kommandozeilen-Tool für alle Disk-Image-Operationen unter macOS.

Image erstellen:

# Leeres DMG (100 MB, HFS+)
hdiutil create -size 100m -fs HFS+ -volname "MeinVolume" MeinImage.dmg

# APFS-Image
hdiutil create -size 100m -fs APFS -volname "MeinVolume" MeinImage.dmg

# Aus Ordner erstellen
hdiutil create -srcfolder ~/MeinOrdner -volname "Backup" Backup.dmg

# Mit Komprimierung
hdiutil create -srcfolder ~/MeinOrdner -format UDZO -volname "Backup" Backup.dmg

Image-Formate:

FormatCodeBeschreibung
Read/writeUDRWVeränderbar
CompressedUDZOzlib-komprimiert, read-only
LZMA compressedULMOStark komprimiert
SparseUDSPWachsend, veränderbar
Sparse bundleUDSBBand-basiert, wachsend
DVD/CD MasterUDTOFür Brennen

Image mounten:

# Standard-Mount (im Finder sichtbar)
hdiutil attach MeinImage.dmg

# Mount ohne Finder-Fenster
hdiutil attach MeinImage.dmg -nobrowse

# Mount an bestimmtem Punkt
hdiutil attach MeinImage.dmg -mountpoint /Volumes/MeinMount

# Read-only mounten
hdiutil attach MeinImage.dmg -readonly

# Ohne Verifizierung (schneller)
hdiutil attach MeinImage.dmg -noverify

Image aushängen:

# Nach Volume-Name
hdiutil detach /Volumes/MeinVolume

# Erzwingen
hdiutil detach /Volumes/MeinVolume -force

# Alle Images aushängen
hdiutil detach -all

Image konvertieren:

# Zu komprimiertem Read-only
hdiutil convert original.dmg -format UDZO -o komprimiert.dmg

# Zu read/write
hdiutil convert readonly.dmg -format UDRW -o readwrite.dmg

# Zu Sparse Bundle
hdiutil convert original.dmg -format UDSB -o sparse.sparsebundle

Image-Größe ändern:

# Größe erweitern (nur sparse/read-write)
hdiutil resize -size 200m MeinImage.dmg

# Auf minimale Größe schrumpfen
hdiutil resize -size min MeinImage.dmg

# Um Betrag erweitern
hdiutil resize -growonly -size 100m MeinImage.dmg

Image-Informationen:

# Image-Info anzeigen
hdiutil imageinfo MeinImage.dmg

# Nur Format
hdiutil imageinfo -format MeinImage.dmg

# Prüfsumme verifizieren
hdiutil verify MeinImage.dmg

5.6 Sparse Images und Sparse Bundles

Sparse Images wachsen dynamisch und belegen nur so viel Platz wie der tatsächliche Inhalt.

Sparse Image erstellen:

# Sparse Image (max 50 GB, wächst bei Bedarf)
hdiutil create -size 50g -type SPARSE -fs APFS -volname "Daten" Daten.sparseimage

# Sparse Bundle (bessere Performance bei Time Machine/Netzwerk)
hdiutil create -size 50g -type SPARSEBUNDLE -fs APFS -volname "Daten" Daten.sparsebundle

Unterschied Sparse Image vs. Sparse Bundle:

MerkmalSparse ImageSparse Bundle
DateistrukturEine DateiOrdner mit Bändern
Backup-freundlich❌ Ganze Datei✅ Nur geänderte Bänder
Time MachineNicht optimalEmpfohlen
NetzwerkLangsamSchneller
KompaktierenMöglichMöglich

Sparse Bundle Struktur:

ls Daten.sparsebundle/
# Info.plist
# bands/
#   0
#   1
#   2
#   ...
# token

Größe kompaktieren (ungenutzten Platz freigeben):

# Erst aushängen
hdiutil detach /Volumes/Daten

# Kompaktieren
hdiutil compact Daten.sparsebundle

# Oder für Sparse Image
hdiutil compact Daten.sparseimage

Bandgröße für Sparse Bundle festlegen:

# 8 MB Bänder (Standard)
hdiutil create -size 50g -type SPARSEBUNDLE -fs APFS \
    -imagekey sparse-band-size=16384 \
    -volname "Daten" Daten.sparsebundle

# sparse-band-size ist in 512-Byte-Sektoren
# 16384 * 512 = 8 MB
# 131072 * 512 = 64 MB

5.7 Verschlüsselte Disk Images

Disk Images können mit AES-128 oder AES-256 verschlüsselt werden – ideal für sensible Daten.

Verschlüsseltes Image erstellen:

# AES-256 (empfohlen)
hdiutil create -size 100m -fs APFS -volname "Geheim" \
    -encryption AES-256 -stdinpass Geheim.dmg
# Passwort eingeben...

# AES-128 (schneller)
hdiutil create -size 100m -fs APFS -volname "Geheim" \
    -encryption AES-128 Geheim.dmg

# Aus Ordner mit Verschlüsselung
hdiutil create -srcfolder ~/Vertraulich \
    -encryption AES-256 -format UDZO \
    -volname "Vertraulich" Vertraulich.dmg

Verschlüsseltes Sparse Bundle (empfohlen für wachsende Daten):

hdiutil create -size 10g -type SPARSEBUNDLE -fs APFS \
    -encryption AES-256 -volname "Tresor" Tresor.sparsebundle

Verschlüsseltes Image mounten:

# Passwort-Abfrage
hdiutil attach Geheim.dmg

# Passwort in Keychain speichern (beim ersten Mount)
# → Option im Dialog wählen

# Passwort per stdin
echo -n "geheim123" | hdiutil attach Geheim.dmg -stdinpass

Passwort ändern:

hdiutil chpass Geheim.dmg
# Altes Passwort eingeben
# Neues Passwort eingeben

Bestehendes Image verschlüsseln:

# Konvertieren mit Verschlüsselung
hdiutil convert Unverschluesselt.dmg \
    -format UDZO -encryption AES-256 \
    -o Verschluesselt.dmg

Sicherheits-Best-Practices:

  • AES-256 für sensitive Daten verwenden
  • Starkes Passwort wählen (Passphrase)
  • Passwort nicht im Schlüsselbund speichern für höchste Sicherheit
  • Image nach Gebrauch aushängen
  • Bei Sparse Bundles: Nach Löschen von Dateien kompaktieren

5.8 DMG-Dateien per Terminal

DMG aus Ordner erstellen (App-Verteilung):

# Einfaches DMG
hdiutil create -srcfolder MeinApp.app \
    -volname "MeinApp" \
    -format UDZO \
    MeinApp.dmg

# Mit Hintergrundbild und Icon-Positionen
# (Erfordert erst ein Read/Write Image)
hdiutil create -srcfolder MeinApp.app \
    -volname "MeinApp" \
    -format UDRW \
    -fs HFS+ \
    temp.dmg

# Mounten und anpassen
hdiutil attach temp.dmg

# Finder-Einstellungen setzen (AppleScript oder manuell)
# ...

# Zu komprimiertem DMG konvertieren
hdiutil convert temp.dmg -format UDZO -o MeinApp.dmg
rm temp.dmg

Internet-Enabled DMG (automatisches Entpacken):

hdiutil internet-enable -yes MeinApp.dmg

DMG verifizieren:

# Prüfsumme verifizieren
hdiutil verify MeinApp.dmg

# Inhalt auflisten ohne Mounten
hdiutil imageinfo MeinApp.dmg

DMG in ISO konvertieren:

hdiutil convert original.dmg -format UDTO -o converted.cdr
mv converted.cdr converted.iso

ISO/CDR erstellen:

# Aus Ordner
hdiutil makehybrid -iso -joliet -o output.iso source_folder/

# CD/DVD-Master für Brennen
hdiutil create -srcfolder Daten -format UDTO -o Master.cdr

Bootfähiges USB-Medium erstellen:

# macOS-Installer auf USB
sudo /Applications/Install\ macOS\ Sonoma.app/Contents/Resources/createinstallmedia \
    --volume /Volumes/USB

# Alternativ mit dd (für Linux ISOs etc.)
diskutil unmountDisk /dev/disk2
sudo dd if=linux.iso of=/dev/rdisk2 bs=1m
diskutil eject /dev/disk2

Praktische Aliase:

# ~/.zshrc

# DMG schnell erstellen
dmg-create() {
    local src="$1"
    local name="${2:-${src:t}}"
    hdiutil create -srcfolder "$src" -volname "$name" -format UDZO "${name}.dmg"
}

# Verschlüsseltes Sparse Bundle erstellen
vault-create() {
    local name="$1"
    local size="${2:-10g}"
    hdiutil create -size "$size" -type SPARSEBUNDLE -fs APFS \
        -encryption AES-256 -volname "$name" "${name}.sparsebundle"
}

# DMG schnell mounten
alias dmg-mount='hdiutil attach'
alias dmg-unmount='hdiutil detach'

Cheatsheet hdiutil:

# Erstellen
hdiutil create -size 100m -fs APFS -volname NAME out.dmg
hdiutil create -srcfolder ORDNER -format UDZO out.dmg
hdiutil create -size 10g -type SPARSEBUNDLE -encryption AES-256 out.sparsebundle

# Mounten
hdiutil attach image.dmg
hdiutil attach image.dmg -mountpoint /Volumes/MOUNT
hdiutil attach image.dmg -readonly

# Aushängen
hdiutil detach /Volumes/NAME

# Konvertieren
hdiutil convert in.dmg -format UDZO -o out.dmg
hdiutil convert in.dmg -format UDRW -o out.dmg

# Größe ändern
hdiutil resize -size 200m image.sparseimage
hdiutil compact image.sparsebundle

# Info
hdiutil imageinfo image.dmg
hdiutil verify image.dmg

Netzwerk (erweitert)

Dieses Kapitel behandelt fortgeschrittene Netzwerk-Tools für DNS-Analyse, Verbindungsdiagnose und Dateiübertragung.

1 DNS & Namensauflösung

DNS (Domain Name System) übersetzt Domainnamen in IP-Adressen. Die folgenden Tools helfen bei der Diagnose von DNS-Problemen.

1.1 nslookup – DNS-Abfragen

nslookup ist ein klassisches Tool für DNS-Abfragen, verfügbar auf praktisch allen Systemen.

Grundlegende Abfragen:

# Einfache Namensauflösung
nslookup example.com
# Server:    192.168.1.1
# Address:   192.168.1.1#53
#
# Non-authoritative answer:
# Name:      example.com
# Address:   93.184.216.34

# IPv6-Adresse abfragen
nslookup -type=AAAA example.com

# Reverse-Lookup (IP → Name)
nslookup 8.8.8.8
# Name: dns.google

Bestimmten DNS-Server verwenden:

# Google DNS
nslookup example.com 8.8.8.8

# Cloudflare DNS
nslookup example.com 1.1.1.1

# Lokaler DNS
nslookup example.com 192.168.1.1

Record-Typen abfragen:

# MX-Records (Mail)
nslookup -type=MX example.com

# NS-Records (Nameserver)
nslookup -type=NS example.com

# TXT-Records (SPF, DKIM, etc.)
nslookup -type=TXT example.com

# SOA-Record (Start of Authority)
nslookup -type=SOA example.com

# CNAME-Record (Alias)
nslookup -type=CNAME www.example.com

# Alle Records
nslookup -type=ANY example.com

Interaktiver Modus:

nslookup
> server 8.8.8.8
> set type=MX
> example.com
> exit

Häufige Record-Typen:

TypBeschreibung
AIPv4-Adresse
AAAAIPv6-Adresse
MXMail-Server
NSNameserver
TXTText-Records (SPF, DKIM)
CNAMEAlias/Weiterleitung
SOAZonen-Autorität
PTRReverse-Lookup
SRVService-Records

1.2 dig – Erweiterte DNS-Analyse

dig (Domain Information Groper) ist das mächtigste DNS-Tool mit detaillierten Ausgaben.

Installation (falls nicht vorhanden):

brew install bind  # enthält dig

Grundlegende Abfragen:

# Standard-Abfrage
dig example.com

# Kurzform (nur Antwort)
dig +short example.com
# 93.184.216.34

# Bestimmter Record-Typ
dig example.com MX
dig example.com AAAA
dig example.com TXT

Ausgabe verstehen:

dig example.com

# ;; QUESTION SECTION:
# ;example.com.                   IN      A
#
# ;; ANSWER SECTION:
# example.com.            86400   IN      A       93.184.216.34
#
# ;; Query time: 23 msec
# ;; SERVER: 192.168.1.1#53(192.168.1.1)
SektionBeschreibung
QUESTIONGestellte Anfrage
ANSWERAntwort-Records
AUTHORITYZuständige Nameserver
ADDITIONALZusätzliche Infos
Query timeAntwortzeit
SERVERVerwendeter DNS-Server

Bestimmten DNS-Server verwenden:

# Mit @server
dig @8.8.8.8 example.com
dig @1.1.1.1 example.com +short

Erweiterte Optionen:

# Alle Sektionen anzeigen
dig example.com +all

# Nur Antwort-Sektion
dig example.com +noall +answer

# Ohne Kommentare
dig example.com +nocomments

# Mit Statistiken
dig example.com +stats

# Trace: Komplette DNS-Auflösung verfolgen
dig example.com +trace

# Reverse-Lookup
dig -x 8.8.8.8

Batch-Abfragen:

# Mehrere Domains
dig example.com google.com github.com +short

# Aus Datei
dig -f domains.txt +short

DNS-Propagation prüfen:

# Bei verschiedenen DNS-Servern abfragen
for dns in 8.8.8.8 1.1.1.1 9.9.9.9; do
    echo "=== $dns ==="
    dig @$dns example.com +short
done

Praktische Beispiele:

# SPF-Record prüfen
dig example.com TXT +short | grep spf

# Alle MX-Records mit Priorität
dig example.com MX +short
# 10 mail.example.com.
# 20 mail2.example.com.

# DNSSEC validieren
dig example.com +dnssec

# TTL (Time-to-Live) anzeigen
dig example.com | grep -A1 "ANSWER SECTION"

1.3 host – Einfache Namensauflösung

host bietet eine einfachere, lesbarere Ausgabe als dig.

Grundlegende Nutzung:

# Namensauflösung
host example.com
# example.com has address 93.184.216.34
# example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946
# example.com mail is handled by 0 .

# Reverse-Lookup
host 8.8.8.8
# 8.8.8.8.in-addr.arpa domain name pointer dns.google.

Record-Typen:

# Nur bestimmten Typ
host -t MX example.com
host -t NS example.com
host -t TXT example.com
host -t AAAA example.com

DNS-Server angeben:

host example.com 8.8.8.8

Optionen:

OptionBeschreibung
-t TYPERecord-Typ
-aAlle Record-Typen
-vVerbose (wie dig)
-4Nur IPv4
-6Nur IPv6

Vergleich der DNS-Tools:

Merkmalnslookupdighost
DetailgradMittelHochNiedrig
LesbarkeitGutTechnischSehr gut
ScriptingMöglichSehr gutGut
VerfügbarkeitÜberallMeist installiertMeist installiert
EmpfehlungLegacyExpertenSchnelle Checks

2 Verbindungen & Ports

2.1 netstat – Netzwerkstatistiken

netstat zeigt Netzwerkverbindungen, Routing-Tabellen und Interface-Statistiken.

tip

Unter macOS ist netstat verfügbar, aber lsof -i ist oft praktischer für Verbindungen. Es zeigt direkt den Prozessnamen und die PID zu jeder Verbindung, während netstat unter macOS diese Information nicht liefert. Außerdem bietet lsof flexiblere Filteroptionen nach Port, Protokoll und Prozess.

Aktive Verbindungen:

# Alle Verbindungen
netstat -an

# Nur TCP
netstat -an -p tcp

# Nur UDP
netstat -an -p udp

# Nur lauschende Ports
netstat -an | grep LISTEN

Ausgabe verstehen:

Proto  Local Address          Foreign Address        State
tcp4   192.168.1.100.52341    93.184.216.34.443     ESTABLISHED
tcp4   *.80                   *.*                    LISTEN
SpalteBeschreibung
ProtoProtokoll (tcp4, tcp6, udp)
Local AddressLokale IP:Port
Foreign AddressRemote IP:Port
StateVerbindungsstatus

Verbindungsstatus:

StatusBeschreibung
LISTENWartet auf Verbindungen
ESTABLISHEDAktive Verbindung
TIME_WAITWarten auf Timeout
CLOSE_WAITWarten auf Schließen
SYN_SENTVerbindungsaufbau

Routing-Tabelle:

netstat -rn
# Destination        Gateway            Flags
# default            192.168.1.1        UGSc
# 192.168.1.0/24     link#6             UCS

Interface-Statistiken:

# Alle Interfaces
netstat -i

# Mit Byte-Zählern
netstat -ib

# Bestimmtes Interface
netstat -I en0

Nützliche Kombinationen:

# Offene Ports nach Anzahl sortiert
netstat -an | grep ESTABLISHED | awk '{print $5}' | cut -d. -f1-4 | sort | uniq -c | sort -rn

# Verbindungen pro Status
netstat -an | awk '/^tcp/ {print $6}' | sort | uniq -c

# Lauschende Dienste
netstat -an -p tcp | grep LISTEN

2.2 lsof -i – Offene Netzwerkverbindungen

lsof (List Open Files) ist unter macOS das bevorzugte Tool für Netzwerkdiagnose.

Grundlegende Nutzung:

# Alle Netzwerkverbindungen
lsof -i

# Mit numerischen Ports (schneller)
lsof -i -n -P

Nach Port filtern:

# Bestimmter Port
lsof -i :80
lsof -i :443
lsof -i :3000

# Port-Bereich
lsof -i :8000-9000

# Mehrere Ports
lsof -i :80 -i :443

Nach Protokoll filtern:

# Nur TCP
lsof -i tcp

# Nur UDP
lsof -i udp

# Nur IPv4
lsof -i 4

# Nur IPv6
lsof -i 6

# Kombiniert
lsof -i tcp:443

Nach Prozess/Benutzer:

# Bestimmter Prozess
lsof -i -c nginx
lsof -i -c python

# Bestimmter Benutzer
lsof -i -u max

# Bestimmte PID
lsof -i -p 1234

Verbindungsstatus:

# Nur lauschende Ports
lsof -i -sTCP:LISTEN

# Nur etablierte Verbindungen
lsof -i -sTCP:ESTABLISHED

# Nur Verbindungen zu bestimmtem Host
lsof -i @192.168.1.100
lsof -i @google.com

Ausgabe verstehen:

COMMAND   PID USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
nginx    1234 root   6u  IPv4  0x1234     0t0  TCP *:http (LISTEN)
Chrome   5678 max   42u  IPv4  0x5678     0t0  TCP 192.168.1.100:52341->93.184.216.34:https (ESTABLISHED)
SpalteBeschreibung
COMMANDProgrammname
PIDProzess-ID
USERBenutzer
FDFile Descriptor
TYPEIPv4/IPv6
NAMEVerbindungsdetails

Praktische Beispiele:

# Was blockiert Port 8080?
lsof -i :8080

# Prozess auf Port beenden
kill $(lsof -t -i :8080)

# Alle Verbindungen eines Programms
lsof -i -c Chrome -n -P

# Verbindungen zu bestimmter Domain
lsof -i @github.com

# Lauschende Ports mit Prozessnamen
lsof -i -P -n | grep LISTEN

# Schnelle Übersicht
lsof -i -n -P | head -20

Aliase für häufige Abfragen:

# ~/.zshrc
alias ports='lsof -i -P -n | grep LISTEN'
alias conn='lsof -i -P -n'
alias whoport='lsof -i -P -n -sTCP:LISTEN | grep'

2.3 nc (netcat) – Netzwerk-Debugging

nc (netcat) ist das “Schweizer Taschenmesser” für Netzwerk-Debugging. Es kann Verbindungen öffnen, auf Ports lauschen und Daten übertragen.

Port-Scan:

# Einzelner Port
nc -zv host.com 80
# Connection to host.com port 80 [tcp/http] succeeded!

# Port-Bereich
nc -zv host.com 20-25

# Schneller Scan (ohne DNS)
nc -zv -n 192.168.1.1 22

# Mit Timeout
nc -zv -w 3 host.com 80

Port-Verfügbarkeit testen:

# HTTP-Port offen?
nc -zv google.com 80 443

# SSH erreichbar?
nc -zv -w 5 server.com 22

Auf Port lauschen (Server):

# Einfacher Server auf Port 1234
nc -l 1234

# Mit Verbose-Output
nc -lv 1234

# UDP-Server
nc -lu 1234

Verbindung herstellen (Client):

# Zu Server verbinden
nc host.com 1234

# Nachricht senden
echo "Hello" | nc host.com 1234

# HTTP-Request manuell
echo -e "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n" | nc example.com 80

Dateiübertragung:

# Empfänger (lauscht)
nc -l 1234 > received_file.txt

# Sender
nc receiver.com 1234 < file.txt

# Mit Fortschritt (via pv)
pv file.txt | nc receiver.com 1234

Chat zwischen zwei Rechnern:

# Rechner A (Server)
nc -l 1234

# Rechner B (Client)
nc rechner-a.local 1234

# Jetzt kann man in beide Richtungen tippen

Banner-Grabbing:

# SSH-Version ermitteln
echo "" | nc -v -w 3 server.com 22
# SSH-2.0-OpenSSH_8.4

# SMTP-Banner
nc -v mail.example.com 25

# HTTP-Header
echo -e "HEAD / HTTP/1.0\r\n\r\n" | nc example.com 80

Proxy / Port-Weiterleitung:

# Einfacher Proxy (erfordert Schleife für mehrere Verbindungen)
nc -l 8080 | nc remote.com 80

Optionen:

OptionBeschreibung
-lListen-Modus (Server)
-vVerbose
-zZero-I/O (nur Scan)
-w SECTimeout
-nKein DNS
-uUDP statt TCP
-p PORTQuell-Port
-kKeep-alive (mehrere Verbindungen)

Debugging-Beispiele:

# Ist Dienst erreichbar?
nc -zv -w 3 db.example.com 5432 && echo "PostgreSQL OK" || echo "FEHLER"

# Alle gängigen Ports scannen
for port in 22 80 443 3306 5432 6379 27017; do
    nc -zv -w 2 server.com $port 2>&1 | grep -q succeeded && echo "Port $port: OFFEN"
done

3 Dateiübertragung

3.1 wget – Dateien herunterladen

wget ist ein robustes Download-Tool mit Unterstützung für Wiederaufnahme und rekursive Downloads.

Installation:

brew install wget

Einfacher Download:

# Datei herunterladen
wget https://example.com/file.zip

# Mit anderem Namen speichern
wget -O myfile.zip https://example.com/file.zip

# In bestimmtes Verzeichnis
wget -P ~/Downloads https://example.com/file.zip

Abgebrochenen Download fortsetzen:

wget -c https://example.com/large-file.zip

Mehrere Dateien:

# Aus Liste
wget -i urls.txt

# Mehrere URLs
wget https://example.com/file1.zip https://example.com/file2.zip

Hintergrund-Download:

# Im Hintergrund mit Log
wget -b -o download.log https://example.com/file.zip

# Fortschritt prüfen
tail -f download.log

Geschwindigkeit begrenzen:

# Max 1 MB/s
wget --limit-rate=1m https://example.com/file.zip

# Max 500 KB/s
wget --limit-rate=500k https://example.com/file.zip

Webseite spiegeln:

# Komplette Website herunterladen
wget --mirror --convert-links --adjust-extension --page-requisites \
     --no-parent https://example.com/

# Kurzform
wget -mkEpnp https://example.com/

Mit Authentifizierung:

# HTTP-Auth
wget --user=username --password=secret https://example.com/protected/file.zip

# Passwort interaktiv
wget --user=username --ask-password https://example.com/protected/file.zip

Cookies und Header:

# Mit Cookie
wget --header="Cookie: session=abc123" https://example.com/

# Custom User-Agent
wget --user-agent="Mozilla/5.0" https://example.com/

# Referer setzen
wget --referer="https://google.com" https://example.com/file.zip

Wichtige Optionen:

OptionBeschreibung
-O FILEAusgabedatei
-P DIRAusgabeverzeichnis
-cFortsetzen
-bHintergrund
-qQuiet (keine Ausgabe)
-vVerbose
--limit-rateGeschwindigkeit begrenzen
-rRekursiv
-l DEPTHRekursionstiefe
-npNo parent (nicht höher)
-kLinks konvertieren
-t NWiederholungsversuche
--timeout=SECTimeout

Alternative: curl

# curl ist auf macOS vorinstalliert
curl -O https://example.com/file.zip

# Mit Ausgabename
curl -o myfile.zip https://example.com/file.zip

# Fortsetzen
curl -C - -O https://example.com/file.zip

# Fortschrittsbalken
curl -# -O https://example.com/file.zip

3.2 scp – Sichere Kopie über SSH

scp kopiert Dateien verschlüsselt über SSH zwischen Rechnern.

Syntax:

scp [Optionen] QUELLE ZIEL

Datei hochladen:

# Lokale Datei auf Server
scp datei.txt user@server:/pfad/

# Mit Port
scp -P 2222 datei.txt user@server:/pfad/

# Mit SSH-Key
scp -i ~/.ssh/key datei.txt user@server:/pfad/

Datei herunterladen:

# Von Server auf lokal
scp user@server:/pfad/datei.txt ./

# Mit anderem Namen
scp user@server:/pfad/datei.txt lokaler-name.txt

Verzeichnis kopieren:

# Rekursiv (ganzes Verzeichnis)
scp -r ordner/ user@server:/pfad/

# Von Server
scp -r user@server:/pfad/ordner/ ./

Zwischen zwei Servern:

scp user1@server1:/pfad/datei.txt user2@server2:/pfad/

Mit SSH-Config:

# Wenn in ~/.ssh/config definiert:
# Host myserver
#     HostName server.com
#     User admin

scp datei.txt myserver:/pfad/

Optionen:

OptionBeschreibung
-rRekursiv (Verzeichnisse)
-P PORTSSH-Port
-i KEYIdentity-File
-pBerechtigungen/Zeiten erhalten
-CKomprimierung
-qQuiet
-vVerbose
-l LIMITBandbreite begrenzen (Kbit/s)

Fortschrittsanzeige:

# Standard: Fortschrittsbalken aktiv
scp large-file.zip user@server:/pfad/

# Ohne Fortschritt
scp -q large-file.zip user@server:/pfad/

Wildcards:

# Alle .txt-Dateien
scp *.txt user@server:/pfad/

# Alle Dateien eines Ordners auf Server
scp user@server:/pfad/*.log ./logs/

3.3 rsync – Erweiterte Synchronisation

Die Grundlagen von rsync werden in rsync – Effiziente Synchronisation und Spiegelung behandelt. Der Fokus dieses Abschnitts liegt auf der Netzwerkübertragung.

rsync über SSH (Standard):

# Auf Server kopieren
rsync -avz ~/projekt/ user@server:/backup/projekt/

# Von Server holen
rsync -avz user@server:/daten/ ~/lokale-kopie/

Mit SSH-Optionen:

# Anderer Port
rsync -avz -e "ssh -p 2222" ~/projekt/ user@server:/backup/

# Mit SSH-Key
rsync -avz -e "ssh -i ~/.ssh/mykey" ~/projekt/ user@server:/backup/

Bandbreite begrenzen:

# Max 1 MB/s
rsync -avz --bwlimit=1000 ~/projekt/ user@server:/backup/

Nur geänderte Dateien:

# Delta-Transfer (nur Unterschiede)
rsync -avz --checksum ~/projekt/ user@server:/backup/

Spiegel mit Löschen:

# Ziel exakt wie Quelle (ACHTUNG!)
rsync -avz --delete ~/projekt/ user@server:/backup/projekt/

# Erst Testlauf
rsync -avzn --delete ~/projekt/ user@server:/backup/projekt/

Bestimmte Dateien aus-/einschließen:

# Ausschließen
rsync -avz --exclude='*.log' --exclude='node_modules/' ~/projekt/ user@server:/backup/

# Aus Datei lesen
rsync -avz --exclude-from='exclude.txt' ~/projekt/ user@server:/backup/

# Nur bestimmte Dateien
rsync -avz --include='*.py' --exclude='*' ~/projekt/ user@server:/backup/

Fortschritt anzeigen:

# Fortschritt pro Datei
rsync -avz --progress ~/projekt/ user@server:/backup/

# Gesamtfortschritt
rsync -avz --info=progress2 ~/projekt/ user@server:/backup/

Backup-Skript:

#!/bin/zsh
# backup-to-server.sh

SRC="$HOME/Documents/"
DEST="user@backup-server:/backups/$(hostname)/"
LOG="$HOME/logs/backup-$(date +%Y%m%d).log"

rsync -avz --delete \
    --exclude='.DS_Store' \
    --exclude='*.tmp' \
    --log-file="$LOG" \
    "$SRC" "$DEST"

echo "Backup abgeschlossen: $(date)" >> "$LOG"

3.4 sftp – Interaktiver Dateitransfer

sftp bietet eine interaktive FTP-ähnliche Oberfläche über SSH.

Verbindung herstellen:

# Standard
sftp user@server

# Mit Port
sftp -P 2222 user@server

# Mit SSH-Key
sftp -i ~/.ssh/key user@server

# Direkt in Verzeichnis
sftp user@server:/pfad/

SFTP-Befehle:

BefehlBeschreibung
ls / dirRemote-Verzeichnis auflisten
llsLokales Verzeichnis auflisten
cd DIRRemote-Verzeichnis wechseln
lcd DIRLokales Verzeichnis wechseln
pwdRemote-Arbeitsverzeichnis
lpwdLokales Arbeitsverzeichnis
get FILEDatei herunterladen
put FILEDatei hochladen
mget PATTERNMehrere Dateien herunterladen
mput PATTERNMehrere Dateien hochladen
mkdir DIRVerzeichnis erstellen
rmdir DIRVerzeichnis löschen
rm FILEDatei löschen
rename OLD NEWUmbenennen
chmod MODE FILEBerechtigungen ändern
chown UID FILEBesitzer ändern
exit / quitBeenden

Typische Session:

sftp user@server
sftp> cd /var/www
sftp> ls
index.html  styles.css  app.js
sftp> get index.html
Fetching /var/www/index.html to index.html
sftp> lcd ~/Desktop
sftp> put new-styles.css styles.css
Uploading new-styles.css to /var/www/styles.css
sftp> exit

Mehrere Dateien:

sftp> mget *.log
sftp> mput *.html

Rekursiv (Verzeichnisse):

sftp> get -r remote-folder/
sftp> put -r local-folder/

Batch-Modus:

# Befehle aus Datei
sftp -b commands.txt user@server

# commands.txt:
# cd /var/www
# get index.html
# put styles.css
# exit

# Einzelner Befehl
echo "get /var/log/app.log" | sftp user@server

Mit SSH-Config:

# Wenn Host in ~/.ssh/config definiert
sftp myserver

Vergleich der Übertragungstools:

Merkmalscprsyncsftp
Interaktiv
Delta-Transfer
Fortsetzen
Batch-fähig
Verzeichnisse-r-r
Bandbreiten-Limit
Löschen am Ziel
AnwendungsfallSchnelle KopieBackup/SyncBrowsing

Empfehlung:

  • scp: Einzelne Dateien schnell kopieren
  • rsync: Backups, Synchronisation, große Datenmengen
  • sftp: Interaktives Arbeiten, Dateien browsen

Datenformate & Streams

Dieses Kapitel behandelt Tools zur Verarbeitung strukturierter Datenformate (JSON, YAML, CSV) sowie Komprimierung und Archivierung.

1 JSON

JSON (JavaScript Object Notation) ist das Standard-Format für APIs und Konfigurationsdateien. jq ist das mächtigste Tool zur JSON-Verarbeitung auf der Kommandozeile.

1.1 jq – Installation und Grundlagen

Installation:

brew install jq

# Version prüfen
jq --version

Grundlegende Nutzung:

# JSON formatieren (Pretty Print)
echo '{"name":"Max","age":30}' | jq .

# Aus Datei lesen
jq . data.json

# Kompakt ausgeben
jq -c . data.json

Beispiel-JSON für diese Referenz:

{
  "users": [
    {"id": 1, "name": "Alice", "email": "alice@example.com", "active": true},
    {"id": 2, "name": "Bob", "email": "bob@example.com", "active": false},
    {"id": 3, "name": "Carol", "email": "carol@example.com", "active": true}
  ],
  "meta": {
    "total": 3,
    "page": 1
  }
}

Grundlegende Filter:

# Ganzes Dokument
jq '.' data.json

# Einzelnes Feld
jq '.meta' data.json
# {"total": 3, "page": 1}

# Verschachteltes Feld
jq '.meta.total' data.json
# 3

# Array
jq '.users' data.json

# Erstes Array-Element
jq '.users[0]' data.json

# Letztes Element
jq '.users[-1]' data.json

# Bereich (Slice)
jq '.users[0:2]' data.json

1.2 Filter und Selektoren

Feld-Zugriff:

# Punkt-Notation
jq '.users[0].name' data.json
# "Alice"

# Ohne Quotes
jq -r '.users[0].name' data.json
# Alice

# Mehrere Felder
jq '.users[0] | .name, .email' data.json
# "Alice"
# "alice@example.com"

Array-Operationen:

# Alle Elemente iterieren
jq '.users[]' data.json

# Bestimmtes Feld aller Elemente
jq '.users[].name' data.json
# "Alice"
# "Bob"
# "Carol"

# Als Array sammeln
jq '[.users[].name]' data.json
# ["Alice", "Bob", "Carol"]

# Array-Länge
jq '.users | length' data.json
# 3

Filtern mit select:

# Nach Bedingung filtern
jq '.users[] | select(.active == true)' data.json

# Nach Wert filtern
jq '.users[] | select(.id > 1)' data.json

# String-Matching
jq '.users[] | select(.name | contains("o"))' data.json

# Regex
jq '.users[] | select(.email | test("@example"))' data.json

# Mehrere Bedingungen (AND)
jq '.users[] | select(.active == true and .id > 1)' data.json

# OR-Bedingung
jq '.users[] | select(.active == true or .id == 2)' data.json

Map und Filter:

# Map über Array
jq '.users | map(.name)' data.json
# ["Alice", "Bob", "Carol"]

# Map mit Transformation
jq '.users | map(.name | ascii_upcase)' data.json
# ["ALICE", "BOB", "CAROL"]

# Filter (select als map)
jq '.users | map(select(.active))' data.json

# Kombiniert
jq '.users | map(select(.active)) | map(.email)' data.json
# ["alice@example.com", "carol@example.com"]

Nützliche Operatoren:

OperatorBeschreibungBeispiel
.Identität (ganzes Objekt)jq '.'
.fieldFeld-Zugriffjq '.name'
.[]Array-Iteratorjq '.users[]'
.[n]Index-Zugriffjq '.[0]'
|Pipe (verketten)jq ‘.users[] | .name’
,Mehrere Ausgabenjq '.name, .age'
?Optional (kein Fehler)jq '.missing?'
//Default-Wertjq '.missing // "N/A"'

1.3 Transformation und Ausgabeformatierung

Objekte konstruieren:

# Neues Objekt erstellen
jq '.users[] | {username: .name, mail: .email}' data.json

# Mit statischen Feldern
jq '.users[] | {type: "user", name: .name}' data.json

# Alle in Array
jq '[.users[] | {username: .name, mail: .email}]' data.json

Werte modifizieren:

# Feld ändern
jq '.meta.page = 2' data.json

# Feld hinzufügen
jq '.meta.timestamp = "2024-01-15"' data.json

# Feld löschen
jq 'del(.meta.page)' data.json

# In Array-Elementen ändern
jq '.users[0].name = "Alicia"' data.json

# Alle Array-Elemente ändern
jq '.users[].active = true' data.json

Ausgabeformatierung:

# Raw-Strings (ohne Quotes)
jq -r '.users[].name' data.json

# Kompakt (eine Zeile)
jq -c '.' data.json

# Tab-Einrückung
jq --tab '.' data.json

# Sortierte Keys
jq -S '.' data.json

# Als TSV (Tab-getrennt)
jq -r '.users[] | [.id, .name, .email] | @tsv' data.json
# 1    Alice    alice@example.com
# 2    Bob      bob@example.com

# Als CSV
jq -r '.users[] | [.id, .name, .email] | @csv' data.json
# 1,"Alice","alice@example.com"

# URI-Encoding
jq -r '.users[0].email | @uri' data.json

# Base64
jq -r '.users[0].name | @base64' data.json

String-Interpolation:

# Template-String
jq -r '.users[] | "User: \(.name) <\(.email)>"' data.json
# User: Alice <alice@example.com>
# User: Bob <bob@example.com>
# User: Carol <carol@example.com>

Eingebaute Funktionen:

FunktionBeschreibungBeispiel
lengthLänge.users | length
keysObjekt-Keys.meta | keys
valuesObjekt-Werte.meta | values
typeTyp ermitteln.users | type
sortArray sortieren.users | sort_by(.name)
uniqueDuplikate entfernen[.users[].active] | unique
reverseArray umkehren.users | reverse
first / lastErstes/Letztes.users | first
addSumme/Konkatenation[.users[].id] | add
min / maxMinimum/Maximum[.users[].id] | max
group_byGruppieren.users | group_by(.active)
ascii_downcaseKleinbuchstaben.name | ascii_downcase
splitString teilen.email | split("@")
joinArray verbinden[.users[].name] | join(", ")
containsEnthält.name | contains("li")
startswithBeginnt mit.email | startswith("a")
sub / gsubErsetzen.name | gsub("a"; "X")

1.4 Praxisbeispiele (API-Responses verarbeiten)

curl mit jq:

# API-Response formatieren
curl -s https://api.github.com/users/octocat | jq .

# Bestimmte Felder extrahieren
curl -s https://api.github.com/users/octocat | jq '{name, bio, location}'

# Array-Response verarbeiten
curl -s https://api.github.com/users/octocat/repos | jq '.[].name'

# Top 5 nach Stars
curl -s https://api.github.com/users/octocat/repos | \
    jq 'sort_by(.stargazers_count) | reverse | .[0:5] | .[].name'

JSON-Konfiguration bearbeiten:

# package.json Version auslesen
jq -r '.version' package.json

# Version erhöhen
jq '.version = "2.0.0"' package.json > package.json.tmp && mv package.json.tmp package.json

# Dependency hinzufügen
jq '.dependencies.lodash = "^4.17.21"' package.json

# Script hinzufügen
jq '.scripts.test = "jest"' package.json

Log-Dateien analysieren:

# JSON-Logs filtern (ein JSON pro Zeile)
cat app.log | jq 'select(.level == "error")'

# Fehler zählen
cat app.log | jq -s 'map(select(.level == "error")) | length'

# Nach Zeitraum filtern
cat app.log | jq 'select(.timestamp > "2024-01-15T00:00:00")'

# Gruppieren nach Level
cat app.log | jq -s 'group_by(.level) | map({level: .[0].level, count: length})'

Mehrere JSON-Dateien:

# Alle JSON-Dateien zusammenführen
jq -s '.' *.json

# In ein Array
jq -s 'add' file1.json file2.json

# Felder aus mehreren Dateien
jq -s 'map(.name)' *.json

Mit Variablen arbeiten:

# Variable übergeben
jq --arg name "Alice" '.users[] | select(.name == $name)' data.json

# Numerische Variable
jq --argjson id 2 '.users[] | select(.id == $id)' data.json

# Aus Umgebungsvariable
export USER_ID=1
jq --argjson id "$USER_ID" '.users[] | select(.id == $id)' data.json

Slurp-Modus (mehrere Objekte als Array):

# Mehrere JSON-Zeilen als Array
echo -e '{"a":1}\n{"a":2}\n{"a":3}' | jq -s '.'
# [{"a":1},{"a":2},{"a":3}]

# Summe berechnen
echo -e '{"a":1}\n{"a":2}\n{"a":3}' | jq -s 'map(.a) | add'
# 6

2 YAML

YAML ist ein menschenlesbares Format, beliebt für Konfigurationsdateien (Docker, Kubernetes, CI/CD).

2.1 yq – Installation und Grundlagen

Es gibt zwei verschiedene yq-Tools. Wir verwenden hier Mike Farah’s Version (Go-basiert):

Installation:

brew install yq

# Version prüfen
yq --version
# yq (https://github.com/mikefarah/yq/) version v4.x

note

Es gibt auch ein Python-basiertes yq (pip install yq), das jq als Backend nutzt. Die Syntax unterscheidet sich leicht.

Beispiel-YAML:

# config.yaml
server:
  host: localhost
  port: 8080
  ssl: true

database:
  host: db.example.com
  port: 5432
  name: myapp

users:
  - name: Alice
    role: admin
  - name: Bob
    role: user

Grundlegende Nutzung:

# YAML formatiert ausgeben
yq '.' config.yaml

# Einzelnes Feld
yq '.server.host' config.yaml
# localhost

# Verschachtelt
yq '.database.port' config.yaml
# 5432

2.2 YAML lesen und bearbeiten

Felder auslesen:

# Einfaches Feld
yq '.server.port' config.yaml

# Array-Element
yq '.users[0].name' config.yaml
# Alice

# Alle Array-Elemente
yq '.users[].name' config.yaml
# Alice
# Bob

# Mit Länge
yq '.users | length' config.yaml
# 2

Werte ändern:

# Feld ändern (in-place)
yq -i '.server.port = 9090' config.yaml

# Ohne in-place (stdout)
yq '.server.port = 9090' config.yaml

# Neues Feld hinzufügen
yq -i '.server.timeout = 30' config.yaml

# Verschachteltes Feld hinzufügen
yq -i '.logging.level = "debug"' config.yaml

# Feld löschen
yq -i 'del(.server.ssl)' config.yaml

Array-Operationen:

# Element hinzufügen
yq -i '.users += [{"name": "Carol", "role": "user"}]' config.yaml

# Element am Anfang
yq -i '.users = [{"name": "Admin", "role": "admin"}] + .users' config.yaml

# Element löschen
yq -i 'del(.users[1])' config.yaml

# Filtern
yq '.users[] | select(.role == "admin")' config.yaml

Umgebungsvariablen einsetzen:

# Variable einsetzen
yq -i '.database.host = env(DB_HOST)' config.yaml

# Mit Default
yq '.database.host // "localhost"' config.yaml

# Aus Umgebung lesen
export DB_HOST="production.db.com"
yq -i '.database.host = env(DB_HOST)' config.yaml

Kommentare und Formatierung:

# Kommentare erhalten
yq '.' config.yaml

# Kommentar hinzufügen
yq -i '.server.port line_comment="HTTP Port"' config.yaml

# Ausgabe-Stil
yq -o=json '.' config.yaml     # Als JSON
yq -o=props '.' config.yaml    # Als Properties
yq -o=xml '.' config.yaml      # Als XML

2.3 Konvertierung JSON ↔ YAML

YAML zu JSON:

# Standard-Konvertierung
yq -o=json '.' config.yaml

# Kompakt
yq -o=json -I=0 '.' config.yaml

# In Datei speichern
yq -o=json '.' config.yaml > config.json

JSON zu YAML:

# JSON einlesen und als YAML ausgeben
yq -P '.' data.json

# Von stdin
echo '{"name": "test", "value": 123}' | yq -P '.'
# name: test
# value: 123

# In Datei
yq -P '.' data.json > data.yaml

Mehrere Dokumente:

# YAML mit mehreren Dokumenten (---)
yq eval-all '.' multi-doc.yaml

# Dokumente zählen
yq eval-all 'documentIndex' multi-doc.yaml | wc -l

# Bestimmtes Dokument
yq 'select(documentIndex == 0)' multi-doc.yaml

Praktische Beispiele:

# Docker-Compose Port ändern
yq -i '.services.web.ports[0] = "9090:80"' docker-compose.yaml

# Kubernetes Replicas erhöhen
yq -i '.spec.replicas = 5' deployment.yaml

# GitHub Actions Workflow bearbeiten
yq -i '.jobs.build.runs-on = "ubuntu-22.04"' .github/workflows/ci.yaml

# Alle Image-Tags auslesen
yq '.spec.template.spec.containers[].image' deployment.yaml

3 CSV

CSV (Comma-Separated Values) ist das universelle Tabellenformat. csvkit bietet eine Suite von Tools zur CSV-Verarbeitung.

3.1 csvkit – Installation und Überblick

Installation:

pip install csvkit --break-system-packages
# oder
brew install csvkit

csvkit-Tools:

ToolBeschreibung
csvlookFormatierte Tabellenansicht
csvcutSpalten auswählen
csvgrepZeilen filtern
csvstatStatistiken
csvsortSortieren
csvjoinTabellen verbinden
csvstackTabellen stapeln
csvformatFormat konvertieren
in2csvZu CSV konvertieren
sql2csvSQL-Abfrage zu CSV
csvsqlSQL auf CSV ausführen

Beispiel-CSV:

id,name,department,salary,start_date
1,Alice,Engineering,75000,2020-03-15
2,Bob,Marketing,65000,2019-07-22
3,Carol,Engineering,80000,2018-11-01
4,David,Sales,70000,2021-01-10
5,Eve,Marketing,72000,2020-09-05

3.2 csvlook, csvcut, csvgrep

csvlook – Formatierte Anzeige:

# Tabelle anzeigen
csvlook employees.csv
# | id | name  | department  | salary | start_date |
# | -- | ----- | ----------- | ------ | ---------- |
# |  1 | Alice | Engineering | 75000  | 2020-03-15 |
# |  2 | Bob   | Marketing   | 65000  | 2019-07-22 |
# ...

# Erste 3 Zeilen
head -4 employees.csv | csvlook

# Spaltenbreite begrenzen
csvlook --max-column-width 15 employees.csv

# Ohne Header-Trennlinie
csvlook --no-header-row employees.csv

csvcut – Spalten auswählen:

# Spaltennamen anzeigen
csvcut -n employees.csv
#   1: id
#   2: name
#   3: department
#   4: salary
#   5: start_date

# Spalten nach Nummer
csvcut -c 1,2,4 employees.csv

# Spalten nach Name
csvcut -c name,salary employees.csv

# Spalten ausschließen
csvcut -C id,start_date employees.csv

# Reihenfolge ändern
csvcut -c salary,name,department employees.csv

csvgrep – Zeilen filtern:

# Nach Wert filtern
csvgrep -c department -m "Engineering" employees.csv

# Regex-Suche
csvgrep -c name -r "^[A-C]" employees.csv

# Invertieren (NOT)
csvgrep -c department -m "Marketing" -i employees.csv

# Mehrere Bedingungen (Pipe)
csvgrep -c department -m "Engineering" employees.csv | csvgrep -c salary -r "^[78]"

Kombinationen:

# Engineering-Mitarbeiter, nur Name und Gehalt
csvgrep -c department -m "Engineering" employees.csv | csvcut -c name,salary | csvlook

# Ausgabe:
# | name  | salary |
# | ----- | ------ |
# | Alice | 75000  |
# | Carol | 80000  |

3.3 csvstat, csvsort, csvjoin

csvstat – Statistiken:

# Vollständige Statistiken
csvstat employees.csv

# Nur bestimmte Spalte
csvstat -c salary employees.csv
# Type of data: Number
# Contains null values: False
# Unique values: 5
# Smallest value: 65000
# Largest value: 80000
# Sum: 362000
# Mean: 72400
# Median: 72000
# ...

# Nur bestimmte Statistik
csvstat --mean -c salary employees.csv
csvstat --sum -c salary employees.csv
csvstat --count employees.csv

csvsort – Sortieren:

# Nach Spalte sortieren
csvsort -c salary employees.csv

# Absteigend
csvsort -c salary -r employees.csv

# Nach mehreren Spalten
csvsort -c department,salary employees.csv

# Numerisch sortieren (automatisch erkannt)
csvsort -c start_date employees.csv

csvjoin – Tabellen verbinden:

# Beispiel: departments.csv
# id,department_name,budget
# 1,Engineering,500000
# 2,Marketing,300000
# 3,Sales,400000

# employees_dept.csv (mit department_id statt name)
# id,name,department_id,salary
# 1,Alice,1,75000
# 2,Bob,2,65000

# Inner Join
csvjoin -c department_id,id employees_dept.csv departments.csv

# Left Join
csvjoin --left -c department_id,id employees_dept.csv departments.csv

# Ausgabespalten wählen
csvjoin -c department_id,id employees_dept.csv departments.csv | csvcut -c name,department_name,salary

csvstack – Tabellen stapeln:

# Zwei CSVs untereinander
csvstack file1.csv file2.csv

# Mit Quell-Kennzeichnung
csvstack -g "Q1,Q2" -n quarter file1.csv file2.csv

csvsql – SQL auf CSV:

# SQL-Abfrage
csvsql --query "SELECT name, salary FROM employees WHERE salary > 70000" employees.csv

# Aggregation
csvsql --query "SELECT department, AVG(salary) as avg_salary FROM employees GROUP BY department" employees.csv

# Join mit SQL
csvsql --query "SELECT e.name, d.department_name
                FROM employees e
                JOIN departments d ON e.department_id = d.id" \
       employees.csv departments.csv

3.4 Alternativen: mlr (Miller), xsv

Miller (mlr) – Mächtiges Datenverarbeitungs-Tool:

# Installation
brew install miller

# CSV lesen
mlr --csv cat employees.csv

# Pretty Print
mlr --icsv --opprint cat employees.csv

# Filtern
mlr --csv filter '$salary > 70000' employees.csv

# Spalten auswählen
mlr --csv cut -f name,salary employees.csv

# Transformieren
mlr --csv put '$bonus = $salary * 0.1' employees.csv

# Gruppieren
mlr --csv stats1 -a mean -f salary -g department employees.csv

# Sortieren
mlr --csv sort -nr salary employees.csv

# JSON-Ausgabe
mlr --icsv --ojson cat employees.csv

Miller Vorteile:

  • Sehr schnell (C-basiert)
  • Unterstützt CSV, JSON, DKVP, Markdown
  • Mächtige DSL für Transformationen
  • Streaming-fähig

xsv – Extrem schnelles CSV-Tool:

# Installation
brew install xsv

# Statistiken (sehr schnell)
xsv stats employees.csv | xsv table

# Spalten
xsv select name,salary employees.csv

# Suchen (schneller als csvgrep)
xsv search -s department "Engineering" employees.csv

# Zählen
xsv count employees.csv

# Häufigkeiten
xsv frequency -s department employees.csv

# Sampling
xsv sample 100 large-file.csv

# Sortieren
xsv sort -s salary -R employees.csv

# Index erstellen (für sehr große Dateien)
xsv index employees.csv
xsv slice -i 1000 employees.csv

xsv Vorteile:

  • Extrem schnell (Rust-basiert)
  • Ideal für große Dateien
  • Index-Unterstützung

Vergleich:

FeaturecsvkitMillerxsv
GeschwindigkeitMittelSchnellSehr schnell
SQL-Support
JSON-Support⚠️
TransformationBasisSehr gutBasis
Große Dateien⚠️
Verfügbarkeitpip/brewbrewbrew

4 Komprimierung & Archivierung

4.1 tar – Archive erstellen und entpacken

tar (Tape Archive) kombiniert mehrere Dateien in ein Archiv. Ursprünglich ohne Kompression, heute meist mit gzip oder xz kombiniert.

Archiv erstellen:

# Ohne Kompression
tar -cvf archiv.tar ordner/
# c = create
# v = verbose
# f = file

# Mit gzip-Kompression
tar -czvf archiv.tar.gz ordner/
# z = gzip

# Mit bzip2-Kompression
tar -cjvf archiv.tar.bz2 ordner/
# j = bzip2

# Mit xz-Kompression (beste Kompression)
tar -cJvf archiv.tar.xz ordner/
# J = xz

# Mehrere Dateien/Ordner
tar -czvf archiv.tar.gz datei1.txt datei2.txt ordner/

# Mit Ausschlüssen
tar -czvf archiv.tar.gz --exclude='*.log' --exclude='node_modules' ordner/

# Aus Liste
tar -czvf archiv.tar.gz -T files.txt

Archiv entpacken:

# tar
tar -xvf archiv.tar

# tar.gz / tgz
tar -xzvf archiv.tar.gz

# tar.bz2
tar -xjvf archiv.tar.bz2

# tar.xz
tar -xJvf archiv.tar.xz

# In bestimmtes Verzeichnis
tar -xzvf archiv.tar.gz -C /ziel/verzeichnis/

# Nur bestimmte Dateien
tar -xzvf archiv.tar.gz pfad/zur/datei.txt

Archiv-Inhalt anzeigen:

# Inhalt auflisten
tar -tvf archiv.tar.gz

# Nur Dateinamen
tar -tf archiv.tar.gz

# Mit grep filtern
tar -tf archiv.tar.gz | grep "\.py$"

Wichtige Optionen:

OptionBeschreibung
-cCreate (erstellen)
-xExtract (entpacken)
-tList (auflisten)
-vVerbose
-f FILEArchivdatei
-zgzip
-jbzip2
-Jxz
-C DIRZielverzeichnis
--excludeAusschließen
-pBerechtigungen erhalten
--strip-components=NN Pfad-Ebenen entfernen

Praktische Beispiele:

# Backup mit Datum
tar -czvf "backup-$(date +%Y%m%d).tar.gz" ~/Documents/

# Nur neue/geänderte Dateien (inkrementell)
tar -czvf backup.tar.gz --newer-mtime="2024-01-01" ~/Documents/

# Über SSH auf Remote-Server
tar -czvf - ordner/ | ssh user@server "cat > backup.tar.gz"

# Von Remote-Server entpacken
ssh user@server "cat backup.tar.gz" | tar -xzvf -

# Pfad-Präfix entfernen beim Entpacken
tar -xzvf archiv.tar.gz --strip-components=1

4.2 gzip / gunzip – Einzeldateien komprimieren

gzip komprimiert einzelne Dateien (ersetzt Original durch .gz-Datei).

Komprimieren:

# Datei komprimieren (Original wird ersetzt)
gzip datei.txt
# Ergebnis: datei.txt.gz

# Original behalten
gzip -k datei.txt

# Kompressionsgrad (1-9, 9=beste)
gzip -9 datei.txt

# Schnellste Kompression
gzip -1 datei.txt

# Mehrere Dateien
gzip file1.txt file2.txt file3.txt

# Rekursiv (alle Dateien in Ordner)
gzip -r ordner/

Dekomprimieren:

# Entpacken
gunzip datei.txt.gz
# oder
gzip -d datei.txt.gz

# Original behalten
gunzip -k datei.txt.gz

# Nach stdout (Original behalten)
gunzip -c datei.txt.gz > datei.txt
zcat datei.txt.gz > datei.txt

Informationen:

# Kompressionsinfos
gzip -l datei.txt.gz
# compressed  uncompressed  ratio  uncompressed_name
#      1234         5678    78.3%  datei.txt

# Integrität prüfen
gzip -t datei.txt.gz

Mit Pipes:

# Komprimiert speichern
cat large-file.log | gzip > large-file.log.gz

# Komprimierte Datei durchsuchen
zgrep "error" logfile.gz
zcat logfile.gz | grep "error"

# Komprimierte Datei anzeigen
zless logfile.gz
zcat logfile.gz | less

4.3 zip / unzip – ZIP-Archive

ZIP ist das universelle Archivformat, kompatibel mit Windows und macOS Finder.

Archiv erstellen:

# Dateien zippen
zip archiv.zip file1.txt file2.txt

# Ordner (rekursiv)
zip -r archiv.zip ordner/

# Mit Passwort
zip -e -r archiv.zip ordner/

# Kompressionsgrad (0-9)
zip -9 -r archiv.zip ordner/

# Keine Kompression (nur archivieren)
zip -0 -r archiv.zip ordner/

# Ausschließen
zip -r archiv.zip ordner/ -x "*.log" -x "*.tmp"

# Versteckte Dateien ausschließen
zip -r archiv.zip ordner/ -x ".*" -x "*/.*"

Archiv entpacken:

# Entpacken
unzip archiv.zip

# In bestimmtes Verzeichnis
unzip archiv.zip -d /ziel/

# Nur bestimmte Dateien
unzip archiv.zip "*.txt"

# Überschreiben ohne Nachfrage
unzip -o archiv.zip

# Nie überschreiben
unzip -n archiv.zip

# Leise
unzip -q archiv.zip

Archiv verwalten:

# Inhalt anzeigen
unzip -l archiv.zip

# Detailliert
unzip -v archiv.zip

# Integrität prüfen
unzip -t archiv.zip

# Datei zum Archiv hinzufügen
zip archiv.zip neue-datei.txt

# Datei aktualisieren (nur wenn neuer)
zip -u archiv.zip datei.txt

# Datei aus Archiv löschen
zip -d archiv.zip datei.txt

Passwortgeschützte Archive:

# Mit Passwort erstellen
zip -e -r geheim.zip ordner/

# Entpacken (Passwort-Prompt)
unzip geheim.zip

# Passwort auf Kommandozeile (unsicher!)
unzip -P "geheim123" geheim.zip

4.4 xz – Hohe Kompression

xz bietet die beste Kompressionsrate, ist aber langsamer als gzip.

Komprimieren:

# Standard-Kompression
xz datei.txt
# Ergebnis: datei.txt.xz

# Original behalten
xz -k datei.txt

# Maximale Kompression (langsam)
xz -9 datei.txt

# Schnelle Kompression
xz -1 datei.txt

# Extreme Kompression
xz -e -9 datei.txt

# Mit Threads (schneller)
xz -T 0 datei.txt  # alle CPUs
xz -T 4 datei.txt  # 4 Threads

Dekomprimieren:

# Entpacken
unxz datei.txt.xz
# oder
xz -d datei.txt.xz

# Original behalten
unxz -k datei.txt.xz

# Nach stdout
xzcat datei.txt.xz

Informationen:

# Details anzeigen
xz -l datei.txt.xz

# Integrität prüfen
xz -t datei.txt.xz

4.5 bzip2 – Alternative Kompression

bzip2 bietet bessere Kompression als gzip, ist aber langsamer.

Komprimieren:

# Komprimieren
bzip2 datei.txt
# Ergebnis: datei.txt.bz2

# Original behalten
bzip2 -k datei.txt

# Kompressionsgrad
bzip2 -9 datei.txt

Dekomprimieren:

# Entpacken
bunzip2 datei.txt.bz2
# oder
bzip2 -d datei.txt.bz2

# Nach stdout
bzcat datei.txt.bz2

4.6 Kombinationen (tar.gz, tar.xz)

Vergleich der Formate:

FormatKompressionGeschwindigkeitVerbreitung
.tarKeineSehr schnellUnix
.tar.gz / .tgzGutSchnellUniversell
.tar.bz2BesserMittelUnix
.tar.xzBesteLangsamModern
.zipGutSchnellUniversell

Empfehlungen:

# Für schnelle Backups
tar -czvf backup.tar.gz ordner/

# Für maximale Kompression (Archive, Downloads)
tar -cJvf archiv.tar.xz ordner/

# Für Windows-Kompatibilität
zip -r archiv.zip ordner/

# Für sehr große Dateien (mit Fortschritt)
tar -cvf - ordner/ | pv | gzip > archiv.tar.gz

Cheatsheet:

# tar erstellen
tar -czvf archiv.tar.gz ordner/     # gzip
tar -cjvf archiv.tar.bz2 ordner/    # bzip2
tar -cJvf archiv.tar.xz ordner/     # xz

# tar entpacken
tar -xzvf archiv.tar.gz             # gzip
tar -xjvf archiv.tar.bz2            # bzip2
tar -xJvf archiv.tar.xz             # xz

# tar auflisten
tar -tzvf archiv.tar.gz

# Einzeldateien
gzip/gunzip datei.txt               # .gz
bzip2/bunzip2 datei.txt             # .bz2
xz/unxz datei.txt                   # .xz

# ZIP
zip -r archiv.zip ordner/
unzip archiv.zip

# Komprimierte Dateien lesen
zcat/zless/zgrep                    # .gz
bzcat/bzless/bzgrep                 # .bz2
xzcat/xzless/xzgrep                 # .xz

Prozesse & Signale

Dieses Kapitel behandelt erweiterte Tools zur Prozessüberwachung, das Signal-System zur Prozesskommunikation und die Verwaltung von Prozessgruppen und Jobs.

1 Erweiterte Monitoring-Tools

Neben den Standard-Tools ps, top und htop bietet macOS spezialisierte Werkzeuge für detailliertes System-Monitoring.

1.1 iotop – I/O-Überwachung

macOS hat kein natives iotop wie Linux, aber es gibt Alternativen:

Alternative 1: sudo iotop (falls installiert)

# Installation (falls verfügbar)
brew install iotop

# Ausführen (benötigt root)
sudo iotop

Alternative 2: fs_usage für I/O-Überwachung

# Disk-I/O aller Prozesse
sudo fs_usage -f diskio

# Bestimmter Prozess
sudo fs_usage -f diskio -w | grep Safari

Alternative 3: iostat – Disk-Statistiken

# Disk-I/O Statistiken
iostat

# Alle 2 Sekunden aktualisieren
iostat -w 2

# Detailliert mit Disk-Namen
iostat -d

# Bestimmtes Intervall, 5 Messungen
iostat 2 5

Ausgabe verstehen:

          disk0       cpu    load average
    KB/t  tps  MB/s  us sy id   1m   5m   15m
   24.00   45  1.06   5  3 92  1.23 1.45 1.67
SpalteBeschreibung
KB/tKilobytes pro Transfer
tpsTransfers pro Sekunde
MB/sMegabytes pro Sekunde
us/sy/idCPU: User/System/Idle %

Alternative 4: Activity Monitor CLI-Daten

# Top-Prozesse nach Disk-Nutzung
top -o disk -l 1 -n 10 | tail -n +13

1.2 vm_stat – Speicherstatistiken

vm_stat zeigt detaillierte Statistiken des virtuellen Speichersystems.

Grundlegende Nutzung:

# Einmalige Ausgabe
vm_stat

# Ausgabe:
# Mach Virtual Memory Statistics: (page size of 16384 bytes)
# Pages free:                               12345.
# Pages active:                             67890.
# Pages inactive:                           11111.
# Pages speculative:                         2222.
# Pages throttled:                              0.
# Pages wired down:                         33333.
# Pages purgeable:                           4444.
# ...

Kontinuierliche Überwachung:

# Alle 1 Sekunde
vm_stat 1

# Alle 5 Sekunden
vm_stat 5

Ausgabe verstehen:

MetrikBeschreibung
Pages freeFreie Speicherseiten
Pages activeAktiv genutzter Speicher
Pages inactiveKürzlich ungenutzt, noch im RAM
Pages speculativeSpekulativ geladene Daten
Pages wired downVom Kernel gesperrt (nicht auslagerbar)
Pages purgeableKann bei Bedarf freigegeben werden
PageinsVon Disk in RAM geladen
PageoutsVon RAM auf Disk ausgelagert
Swapins/SwapoutsSwap-Aktivität

In lesbare Werte umrechnen:

# Page-Größe ermitteln
pagesize=$(vm_stat | head -1 | grep -oE '[0-9]+')

# Freier Speicher in MB
free_pages=$(vm_stat | grep "Pages free" | awk '{print $3}' | tr -d '.')
echo "Frei: $(( free_pages * pagesize / 1024 / 1024 )) MB"

Skript für lesbare Ausgabe:

#!/bin/zsh
# mem-usage.sh

pagesize=$(pagesize)

get_pages() {
    vm_stat | grep "$1" | awk '{print $NF}' | tr -d '.'
}

free=$(($(get_pages "Pages free") * pagesize / 1024 / 1024))
active=$(($(get_pages "Pages active") * pagesize / 1024 / 1024))
inactive=$(($(get_pages "Pages inactive") * pagesize / 1024 / 1024))
wired=$(($(get_pages "Pages wired") * pagesize / 1024 / 1024))

echo "Speicher-Nutzung:"
echo "  Frei:     ${free} MB"
echo "  Aktiv:    ${active} MB"
echo "  Inaktiv:  ${inactive} MB"
echo "  Wired:    ${wired} MB"

1.3 fs_usage – Dateisystem-Aktivität

fs_usage zeigt Dateisystem-Aktivität in Echtzeit – ideal zum Debuggen von I/O-Problemen.

Grundlegende Nutzung:

# Alle Dateisystem-Aktivität (benötigt root)
sudo fs_usage

# Nur Disk-I/O
sudo fs_usage -f diskio

# Nur Netzwerk
sudo fs_usage -f network

# Nur Dateisystem-Calls
sudo fs_usage -f filesys

Nach Prozess filtern:

# Bestimmter Prozess (Name)
sudo fs_usage -w Safari

# Bestimmte PID
sudo fs_usage -w -p 1234

# Mehrere Prozesse
sudo fs_usage -w Safari Chrome Firefox

# Prozess ausschließen
sudo fs_usage -e Safari

Ausgabe-Optionen:

# Breite Ausgabe (vollständige Pfade)
sudo fs_usage -w

# Mit Timestamps
sudo fs_usage -t

# Kombiniert
sudo fs_usage -w -f diskio -t

Ausgabe verstehen:

14:23:45  RdData     D=0x012345  B=0x1000  /path/to/file   Safari
14:23:45  open       F=3         /path/to/file             Safari
14:23:45  read       F=3  B=0x400                          Safari
14:23:45  close      F=3                                   Safari
SpalteBeschreibung
TimestampZeitstempel
Operationread, write, open, close, stat, etc.
F=File Descriptor
B=Bytes
D=Disk Block
PfadBetroffene Datei
ProzessProzessname

Praktische Anwendungen:

# Was liest/schreibt ein Prozess?
sudo fs_usage -w -f diskio Spotlight

# Welche Dateien öffnet eine App beim Start?
sudo fs_usage -w -f filesys Safari 2>&1 | grep open

# Netzwerkaktivität eines Prozesses
sudo fs_usage -w -f network curl

# In Datei protokollieren
sudo fs_usage -w Safari > fs_log.txt 2>&1 &
# Später: kill %1

1.4 nettop – Netzwerk pro Prozess

nettop zeigt Netzwerkverbindungen und Bandbreite pro Prozess – wie top für Netzwerk.

Grundlegende Nutzung:

# Interaktive Ansicht
nettop

# Nicht-interaktiv (einmalig)
nettop -l 1

# Alle 2 Sekunden, 5 Messungen
nettop -l 5 -s 2

Filter-Optionen:

# Nur TCP
nettop -t tcp

# Nur UDP
nettop -t udp

# Bestimmter Prozess
nettop -p Safari

# Nur mit Traffic (Delta-Modus)
nettop -d

Ausgabe-Optionen:

# Nur bestimmte Spalten
nettop -c all       # Alle Spalten
nettop -c bytes_in,bytes_out

# Sortieren
nettop -o bytes_in  # Nach eingehenden Bytes

# JSON-Ausgabe
nettop -j -l 1

Interaktive Tastenkürzel:

TasteFunktion
qBeenden
dDelta-Modus (nur Änderungen)
eProzesse ein-/ausklappen
hHilfe
pNach Prozess sortieren
bNach Bytes sortieren

Ausgabe verstehen:

                        bytes_in  bytes_out  rx_dupe  ...
Safari.1234               1.2MB     45.6KB       0
  tcp4  192.168.1.100:52341  ->  93.184.216.34:443   512KB   12KB
Chrome.5678               800KB    123.4KB       0

Praktische Beispiele:

# Top-Netzwerknutzer
nettop -l 1 -t wifi | head -20

# Verbindungen eines Prozesses
nettop -p Safari -l 1

# Bandbreite überwachen
watch -n 1 "nettop -l 1 -d | head -15"

Alternativen:

# lsof für Netzwerkverbindungen
lsof -i -n -P

# netstat
netstat -an -p tcp

# Aktivitätsmonitor CLI (Netzwerk-Tab)
nettop -c state,bytes_in,bytes_out -l 1

2 Signale

Signale sind der primäre Mechanismus zur Kommunikation mit Prozessen unter Unix. Sie können Prozesse beenden, pausieren, fortsetzen oder benutzerdefinierte Aktionen auslösen.

2.1 Signal-Übersicht (SIGTERM, SIGKILL, SIGHUP, …)

Wichtige Signale:

SignalNummerBeschreibungAbfangbar
SIGHUP1Hangup (Terminal geschlossen)Ja
SIGINT2Interrupt (Ctrl+C)Ja
SIGQUIT3Quit mit Core-Dump (Ctrl+)Ja
SIGKILL9Sofort beenden (nicht abfangbar!)Nein
SIGTERM15Sauber beenden (Standard)Ja
SIGSTOP17Prozess anhaltenNein
SIGTSTP18Terminal Stop (Ctrl+Z)Ja
SIGCONT19FortsetzenJa
SIGUSR130Benutzerdefiniert 1Ja
SIGUSR231Benutzerdefiniert 2Ja
SIGCHLD20Kind-Prozess beendetJa
SIGALRM14Timer abgelaufenJa
SIGPIPE13Schreiben in geschlossene PipeJa

Alle Signale anzeigen:

kill -l
# HUP INT QUIT ILL TRAP ABRT EMT FPE KILL BUS SEGV SYS PIPE ALRM TERM ...

Signal-Verhalten:

SignalTypische Reaktion
SIGTERMSauberes Herunterfahren, Aufräumen
SIGKILLSofortiger Tod, keine Aufräumarbeiten
SIGHUPKonfiguration neu laden (Daemons)
SIGINTInteraktiver Abbruch
SIGSTOP/CONTPausieren/Fortsetzen

Best Practice:

1. Erst SIGTERM (15) versuchen – gibt Prozess Zeit zum Aufräumen
2. Warten (einige Sekunden)
3. Falls nötig: SIGKILL (9) – als letzter Ausweg

2.2 kill – Signale an Prozesse senden

Trotz des Namens kann kill alle Signale senden, nicht nur beenden.

Syntax:

kill [-SIGNAL] PID...

Grundlegende Nutzung:

# Standard: SIGTERM (15)
kill 1234

# SIGKILL (sofort beenden)
kill -9 1234
kill -KILL 1234

# SIGHUP (Konfiguration neu laden)
kill -1 1234
kill -HUP 1234

# SIGSTOP (pausieren)
kill -STOP 1234

# SIGCONT (fortsetzen)
kill -CONT 1234

Mehrere Prozesse:

# Mehrere PIDs
kill 1234 5678 9012

# Alle Prozesse einer Gruppe
kill -TERM -1234    # Negative PID = Prozessgruppe

# Mit $! (letzter Hintergrundprozess)
sleep 100 &
kill $!

Existenz prüfen:

# Signal 0 prüft nur, ob Prozess existiert
kill -0 1234 && echo "Prozess existiert" || echo "Nicht gefunden"

2.3 kill -s – Signal nach Name

Signale können auch mit -s und Namen angegeben werden.

Syntax:

kill -s SIGNALNAME PID

Beispiele:

# Nach Name
kill -s TERM 1234
kill -s KILL 1234
kill -s HUP 1234

# Äquivalent zu
kill -TERM 1234
kill -15 1234

Alle verfügbaren Signalnamen:

kill -l
# 1) SIGHUP     2) SIGINT     3) SIGQUIT    4) SIGILL
# 5) SIGTRAP    6) SIGABRT    7) SIGEMT     8) SIGFPE
# 9) SIGKILL   10) SIGBUS    11) SIGSEGV   12) SIGSYS
# ...

2.4 pkill – Prozesse nach Name beenden

pkill sendet Signale an Prozesse basierend auf Namen oder anderen Kriterien.

Syntax:

pkill [Optionen] MUSTER

Grundlegende Nutzung:

# Nach Prozessname (Teilmatch)
pkill Safari
pkill -9 Safari      # SIGKILL

# Exakter Name
pkill -x Safari

# Case-insensitive
pkill -i safari

Erweiterte Filter:

# Nach Benutzer
pkill -u max Safari

# Nach Gruppe
pkill -g staff Firefox

# Nach Terminal
pkill -t ttys001

# Ältere als X Sekunden
pkill -o -older 3600 process

# Jüngste Instanz
pkill -n process

# Eltern-PID
pkill -P 1234

Optionen:

OptionBeschreibung
-SIGNALSignal angeben (-9, -HUP, etc.)
-u USERNach Benutzer
-g GROUPNach Gruppe
-t TTYNach Terminal
-P PPIDNach Eltern-PID
-xExakter Name
-iCase-insensitive
-nNur neuester Prozess
-oNur ältester Prozess
-fVollständige Kommandozeile matchen

Trockenlauf mit pgrep:

# Erst prüfen was gematcht wird
pgrep -l Safari
# 1234 Safari

# Dann killen
pkill Safari

2.5 killall – Alle Prozesse eines Namens

killall beendet alle Prozesse mit exakt dem angegebenen Namen.

Syntax:

killall [Optionen] PROZESSNAME

Grundlegende Nutzung:

# Alle Safari-Prozesse beenden
killall Safari

# Mit SIGKILL
killall -9 Safari
killall -KILL Safari

# Mit SIGHUP
killall -HUP nginx

Optionen:

# Interaktiv (Bestätigung)
killall -i Safari

# Verbose
killall -v Safari

# Nach Benutzer
killall -u max Safari

# Ältere als X Sekunden
killall -o 1h Safari    # älter als 1 Stunde
killall -y 5m Safari    # jünger als 5 Minuten

# Regex
killall -r "^Safari"

Optionen-Übersicht:

OptionBeschreibung
-SIGNALSignal angeben
-vVerbose
-iInteraktiv (fragen)
-u USERNach Benutzer
-o TIMEÄlter als
-y TIMEJünger als
-rRegex-Match
-zAuch Zombies

Unterschied pkill vs killall:

Merkmalpkillkillall
MatchingTeilstring/RegexExakter Name (Standard)
KommandozeileMit -fNein
FilterUmfangreichBasis
PortabilitätPOSIX-ähnlichBSD/macOS

Praktische Beispiele:

# Alle Python-Prozesse
killall python3

# Alle Hintergrund-Jobs des Users
killall -u $(whoami)

# Nginx graceful restart
killall -HUP nginx

# Alle Node.js-Prozesse älter als 1 Tag
killall -o 24h node

3 Prozessgruppen & Jobs

3.1 Vordergrund- und Hintergrundprozesse

Vordergrundprozess:

  • Blockiert das Terminal
  • Empfängt Tastatureingaben
  • Ctrl+C sendet SIGINT
  • Ctrl+Z sendet SIGTSTP (pausieren)

Hintergrundprozess:

  • Terminal bleibt nutzbar
  • Kein direkter Tastaturzugriff
  • Läuft parallel

Prozess im Hintergrund starten:

# Mit & am Ende
long-running-command &

# Beispiel
sleep 100 &
# [1] 12345   (Job-Nummer und PID)

Jobs verwalten:

# Alle Jobs anzeigen
jobs
# [1]  + running    sleep 100
# [2]  - running    another-command

# Mit PIDs
jobs -p

# Mit Status
jobs -l

Zwischen Vorder- und Hintergrund wechseln:

# Prozess starten
sleep 100

# Pausieren mit Ctrl+Z
^Z
# [1]  + 12345 suspended  sleep 100

# Im Hintergrund fortsetzen
bg
bg %1       # Bestimmter Job

# In den Vordergrund holen
fg
fg %1       # Bestimmter Job

Job-Referenzen:

ReferenzBedeutung
%1Job Nummer 1
%+ oder %%Aktueller Job
%-Vorheriger Job
%stringJob der mit “string” beginnt
%?stringJob der “string” enthält

Beispiele:

# Drei Jobs starten
sleep 100 &
vim file.txt &
python script.py &

# Jobs anzeigen
jobs
# [1]    running    sleep 100
# [2]  - running    vim file.txt
# [3]  + running    python script.py

# Vim in Vordergrund
fg %2
fg %vi       # Nach Name

# Python beenden
kill %3
kill %?python

3.2 nohup – Prozesse vom Terminal lösen

nohup (No Hangup) verhindert, dass ein Prozess beendet wird wenn das Terminal geschlossen wird.

Grundlegende Nutzung:

# Prozess startet und läuft nach Terminal-Schließung weiter
nohup command &

# Beispiel
nohup ./long-script.sh &
# nohup: ignoring input and appending output to 'nohup.out'

Ausgabe umleiten:

# Standard: Ausgabe geht nach nohup.out
nohup command &

# Eigene Log-Datei
nohup command > output.log 2>&1 &

# Ausgabe verwerfen
nohup command > /dev/null 2>&1 &

Typischer Workflow:

# 1. Prozess mit nohup starten
nohup ./backup.sh > backup.log 2>&1 &

# 2. PID notieren
echo $!
# 12345

# 3. Terminal schließen (Prozess läuft weiter)
exit

# 4. Später: Status prüfen
ps -p 12345
tail -f backup.log

Praktisches Beispiel:

# Langer Download
nohup wget -c https://example.com/large-file.zip > wget.log 2>&1 &

# Server starten
nohup python -m http.server 8000 > server.log 2>&1 &

# Backup-Skript
nohup rsync -avz /source/ /backup/ > rsync.log 2>&1 &

3.3 disown – Jobs aus Shell entfernen

disown entfernt einen Job aus der Job-Tabelle der Shell, sodass er nicht beendet wird wenn die Shell beendet wird.

Unterschied zu nohup:

Merkmalnohupdisown
ZeitpunktVor dem StartNach dem Start
SIGHUP ignorierenJaMit -h
Job-TabelleNormalEntfernt
Ausgabenohup.outUnverändert

Grundlegende Nutzung:

# Job starten
long-command &

# Aus Job-Tabelle entfernen
disown

# Bestimmten Job
disown %1

# SIGHUP ignorieren (Job bleibt in Liste)
disown -h %1

# Alle Jobs
disown -a

Typischer Workflow:

# 1. Prozess gestartet (vergessen nohup zu nutzen)
./script.sh
^Z
bg

# 2. Jetzt disown um ihn zu behalten
disown -h %1

# 3. Shell kann sicher beendet werden
exit

Praktisches Beispiel:

# Server versehentlich ohne nohup gestartet
python server.py &

# Oops! Terminal schließen würde Server beenden
# Lösung: disown
disown -h

# Jetzt sicher Terminal schließen
exit

Nachträglich von Terminal lösen:

# Prozess läuft im Vordergrund
long-running-process

# Ctrl+Z pausiert
^Z
# [1]  + suspended  long-running-process

# Im Hintergrund fortsetzen
bg

# Von Shell lösen
disown -h

# Terminal kann geschlossen werden

3.4 Prozessgruppen und Session-IDs

Konzepte:

Session (SID)
    └── Prozessgruppe (PGID)
            └── Prozess (PID)
            └── Prozess (PID)
    └── Prozessgruppe (PGID)
            └── Prozess (PID)
BegriffBeschreibung
PIDProcess ID – eindeutige Prozess-Kennung
PPIDParent PID – PID des Elternprozesses
PGIDProcess Group ID – Gruppe zusammengehöriger Prozesse
SIDSession ID – Login-Session

IDs anzeigen:

# Aktuelle IDs
echo "PID: $$"
echo "PPID: $PPID"

# Mit ps
ps -o pid,ppid,pgid,sid,comm

# Für bestimmten Prozess
ps -o pid,ppid,pgid,sid,comm -p 1234

Prozessgruppen verstehen:

# Pipeline erstellt Prozessgruppe
cat file | grep pattern | sort

# Alle drei Prozesse haben gleiche PGID
ps -o pid,pgid,comm
# PID   PGID  COMM
# 1001  1001  cat
# 1002  1001  grep
# 1003  1001  sort

Signal an Prozessgruppe senden:

# Negative PID = Prozessgruppe
kill -TERM -1001

# Alle Prozesse der Gruppe beenden
kill -9 -$(ps -o pgid= -p $PID)

Sessions verstehen:

  • Jedes Terminal startet eine neue Session
  • Session-Leader ist meist die Shell
  • Alle von der Shell gestarteten Prozesse gehören zur Session
# Neue Session erstellen (für Daemons)
setsid command

# Prozess von Terminal lösen
setsid ./daemon.sh

Prozessbaum anzeigen:

# Mit pstree (falls installiert)
brew install pstree
pstree

# Mit ps
ps -axjf

# Kinder eines Prozesses
pgrep -P 1234
ps --ppid 1234

Waisen- und Zombie-Prozesse:

TypBeschreibungStatus
WaiseElternprozess beendet, init übernimmtNormal
ZombieBeendet, aber Eltern hat Exit-Code nicht gelesenZ in ps
# Zombies finden
ps aux | awk '$8=="Z"'

# Oder
ps -eo pid,ppid,stat,comm | grep Z

Zombie-Prozesse bereinigen:

Zombies können nicht direkt beendet werden. Lösungen:

# 1. SIGCHLD an Elternprozess
kill -SIGCHLD $(ps -o ppid= -p $ZOMBIE_PID)

# 2. Elternprozess beenden (Zombie wird von init übernommen)
kill $(ps -o ppid= -p $ZOMBIE_PID)

# 3. Im schlimmsten Fall: Neustart

Daemon-Muster (doppelter Fork):

#!/bin/zsh
# daemon.sh - Prozess vollständig vom Terminal lösen

(
    # Erster Fork
    cd /
    umask 0
    exec setsid ./actual-daemon.sh &
) &

# Shell kann sofort beenden
exit 0

Zusammenfassung Job-Control:

# Starten
command &                    # Im Hintergrund
nohup command &              # Immun gegen SIGHUP

# Kontrolle
jobs                         # Jobs anzeigen
fg %n                        # In Vordergrund
bg %n                        # Im Hintergrund fortsetzen
Ctrl+Z                       # Pausieren
Ctrl+C                       # Abbrechen

# Loslösen
disown %n                    # Aus Job-Liste entfernen
disown -h %n                 # SIGHUP ignorieren

# Beenden
kill %n                      # Job beenden
kill -9 %n                   # Erzwungen

Sicherheit & Verschlüsselung

Dieses Kapitel behandelt Werkzeuge für Verschlüsselung, digitale Signaturen und sichere Systemadministration.

1 GPG (GnuPG)

GnuPG (GNU Privacy Guard) ist die freie Implementierung des OpenPGP-Standards für asymmetrische Verschlüsselung und digitale Signaturen.

1.1 Installation und Schlüsselerstellung

Installation:

brew install gnupg

# Version prüfen
gpg --version

Optionale GUI-Tools:

# GPG Suite (macOS GUI)
brew install --cask gpg-suite

# Oder nur Pinentry für Passphrase-Eingabe
brew install pinentry-mac

Schlüsselpaar generieren:

# Interaktiver Assistent
gpg --full-generate-key

# Ausgabe:
# Bitte wählen Sie, welche Art von Schlüssel Sie möchten:
#    (1) RSA und RSA
#    (2) DSA und Elgamal
#    (3) DSA (nur signieren)
#    (4) RSA (nur signieren)
#    (9) ECC (signieren und verschlüsseln) *Standard*
#   (10) ECC (nur signieren)
# Ihre Auswahl? 9

Empfohlene Einstellungen:

EinstellungEmpfehlung
AlgorithmusECC (Curve 25519) oder RSA 4096
Gültigkeit1-2 Jahre (kann verlängert werden)
NameVollständiger Name
E-MailHaupt-E-Mail-Adresse
PassphraseStark und einzigartig

Schnelle Schlüsselerstellung:

# Schnell mit Defaults
gpg --quick-generate-key "Max Mustermann <max@example.com>"

# Mit spezifischen Optionen
gpg --quick-generate-key "Max Mustermann <max@example.com>" rsa4096 cert 2y

Schlüssel nach Erstellung:

# Öffentliche Schlüssel anzeigen
gpg --list-keys
gpg -k

# Private Schlüssel anzeigen
gpg --list-secret-keys
gpg -K

# Ausgabe:
# pub   ed25519 2024-01-15 [SC] [expires: 2026-01-15]
#       ABCD1234EFGH5678IJKL9012MNOP3456QRST7890
# uid           [ultimate] Max Mustermann <max@example.com>
# sub   cv25519 2024-01-15 [E] [expires: 2026-01-15]

Schlüssel-IDs verstehen:

LängeBeispielVerwendung
Fingerprint (40)ABCD1234...7890Vollständige Identifikation
Long ID (16)MNOP3456QRST7890Eindeutig genug
Short ID (8)QRST7890Veraltet, Kollisionsgefahr
E-Mailmax@example.comBequem, aber nicht eindeutig

1.2 Dateien verschlüsseln und entschlüsseln

Asymmetrisch verschlüsseln (für Empfänger):

# Für einen Empfänger
gpg --encrypt --recipient max@example.com datei.txt
# Ergebnis: datei.txt.gpg

# Kurzform
gpg -e -r max@example.com datei.txt

# Für mehrere Empfänger
gpg -e -r alice@example.com -r bob@example.com datei.txt

# Mit ASCII-Armor (Textformat)
gpg -e -a -r max@example.com datei.txt
# Ergebnis: datei.txt.asc

# Ausgabedatei angeben
gpg -e -r max@example.com -o geheim.gpg datei.txt

Asymmetrisch entschlüsseln:

# Standard (fragt nach Passphrase)
gpg --decrypt datei.txt.gpg
# Ausgabe auf stdout

# In Datei speichern
gpg -d datei.txt.gpg > datei.txt
gpg -d -o datei.txt datei.txt.gpg

# Kurzform
gpg datei.txt.gpg
# Erstellt automatisch datei.txt

Symmetrisch verschlüsseln (mit Passwort):

# Mit Passwort verschlüsseln
gpg --symmetric datei.txt
gpg -c datei.txt
# Fragt nach Passphrase, erstellt datei.txt.gpg

# Mit spezifischem Algorithmus
gpg -c --cipher-algo AES256 datei.txt

# Als ASCII
gpg -c -a datei.txt

Symmetrisch entschlüsseln:

gpg -d datei.txt.gpg
gpg -d -o datei.txt datei.txt.gpg

Kombiniert (symmetrisch + asymmetrisch):

# Sowohl mit Passwort als auch für Empfänger
gpg -c -e -r max@example.com datei.txt

Verzeichnisse verschlüsseln:

# Erst tar, dann gpg
tar -czvf - ordner/ | gpg -c -o ordner.tar.gz.gpg

# Entschlüsseln und entpacken
gpg -d ordner.tar.gz.gpg | tar -xzvf -

1.3 Signaturen erstellen und prüfen

Digitale Signaturen beweisen Authentizität und Integrität.

Datei signieren:

# Detached Signature (separate .sig Datei)
gpg --detach-sign datei.txt
# Ergebnis: datei.txt.sig

# ASCII-Format
gpg --detach-sign -a datei.txt
# Ergebnis: datei.txt.asc

# Klartext-Signatur (Signatur + Original)
gpg --clearsign datei.txt
# Ergebnis: datei.txt.asc (enthält Original + Signatur)

# Signatur eingebettet (binär)
gpg --sign datei.txt
# Ergebnis: datei.txt.gpg

Signatur prüfen:

# Detached Signature prüfen
gpg --verify datei.txt.sig datei.txt

# Ausgabe bei gültiger Signatur:
# gpg: Signature made Mon Jan 15 10:30:00 2024 CET
# gpg:                using EDDSA key ABCD...7890
# gpg: Good signature from "Max Mustermann <max@example.com>"

# Clearsign oder eingebettete Signatur
gpg --verify datei.txt.asc

# Signierte Datei extrahieren und prüfen
gpg --decrypt datei.txt.gpg

Signieren und Verschlüsseln:

# Verschlüsseln + Signieren
gpg -e -s -r empfaenger@example.com datei.txt

# Entschlüsseln prüft automatisch Signatur
gpg -d datei.txt.gpg

Git-Commits signieren:

# GPG-Schlüssel für Git konfigurieren
git config --global user.signingkey ABCD1234EFGH5678

# Einzelnen Commit signieren
git commit -S -m "Signierter Commit"

# Alle Commits signieren
git config --global commit.gpgsign true

# Signaturen anzeigen
git log --show-signature

1.4 Schlüsselverwaltung

Schlüssel exportieren:

# Öffentlichen Schlüssel exportieren (zum Teilen)
gpg --export -a max@example.com > publickey.asc
gpg --export --armor ABCD1234 > publickey.asc

# Privaten Schlüssel exportieren (Backup!)
gpg --export-secret-keys -a max@example.com > privatekey.asc

# Alle Schlüssel exportieren
gpg --export -a > all-public-keys.asc
gpg --export-secret-keys -a > all-private-keys.asc

Schlüssel importieren:

# Öffentlichen Schlüssel importieren
gpg --import publickey.asc

# Privaten Schlüssel importieren
gpg --import privatekey.asc

# Von Keyserver
gpg --keyserver hkps://keys.openpgp.org --recv-keys ABCD1234EFGH5678

Schlüssel auf Keyserver hochladen:

gpg --keyserver hkps://keys.openpgp.org --send-keys ABCD1234EFGH5678

Schlüssel bearbeiten:

# Interaktiver Editor
gpg --edit-key max@example.com

# Verfügbare Befehle:
# uid       - UID auswählen
# trust     - Vertrauensstufe setzen
# expire    - Ablaufdatum ändern
# passwd    - Passphrase ändern
# adduid    - E-Mail hinzufügen
# deluid    - E-Mail entfernen
# save      - Speichern und beenden
# quit      - Ohne Speichern beenden

Vertrauensstufen:

StufeBedeutung
UnknownNicht bewertet
NeverNicht vertrauenswürdig
MarginalTeilweise vertrauenswürdig
FullVoll vertrauenswürdig
UltimateEigener Schlüssel
# Vertrauen setzen
gpg --edit-key alice@example.com
gpg> trust
# Stufe wählen
gpg> save

Schlüssel widerrufen:

# Widerrufszertifikat erstellen (bei Erstellung!)
gpg --gen-revoke ABCD1234 > revoke.asc

# Schlüssel widerrufen (wenn kompromittiert)
gpg --import revoke.asc
gpg --keyserver hkps://keys.openpgp.org --send-keys ABCD1234

Schlüssel löschen:

# Öffentlichen Schlüssel löschen
gpg --delete-keys ABCD1234

# Privaten Schlüssel löschen
gpg --delete-secret-keys ABCD1234

# Beides
gpg --delete-secret-and-public-keys ABCD1234

GPG-Agent konfigurieren:

# ~/.gnupg/gpg-agent.conf
default-cache-ttl 3600
max-cache-ttl 86400
pinentry-program /opt/homebrew/bin/pinentry-mac
# Agent neu starten
gpgconf --kill gpg-agent
gpg-agent --daemon

2 OpenSSL

OpenSSL ist das Schweizer Taschenmesser für Kryptographie: Zertifikate, Verschlüsselung, Hashes und mehr.

2.1 Zertifikate anzeigen und prüfen

Zertifikat einer Website anzeigen:

# Zertifikat abrufen und anzeigen
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | \
    openssl x509 -noout -text

# Nur wichtige Infos
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | \
    openssl x509 -noout -subject -issuer -dates

# Ausgabe:
# subject=CN = example.com
# issuer=C = US, O = DigiCert Inc, CN = DigiCert TLS RSA SHA256 2020 CA1
# notBefore=Jan 15 00:00:00 2024 GMT
# notAfter=Jan 15 23:59:59 2025 GMT

Lokales Zertifikat anzeigen:

# PEM-Datei
openssl x509 -in certificate.pem -noout -text

# DER-Format
openssl x509 -in certificate.der -inform DER -noout -text

# Nur bestimmte Felder
openssl x509 -in cert.pem -noout -subject
openssl x509 -in cert.pem -noout -issuer
openssl x509 -in cert.pem -noout -dates
openssl x509 -in cert.pem -noout -fingerprint
openssl x509 -in cert.pem -noout -serial

Zertifikatskette prüfen:

# Kette verifizieren
openssl verify -CAfile ca-bundle.crt certificate.pem

# Mit Zwischenzertifikaten
openssl verify -CAfile root.crt -untrusted intermediate.crt server.crt

TLS-Verbindung testen:

# Verbindung und Handshake testen
openssl s_client -connect example.com:443

# Mit bestimmtem Protokoll
openssl s_client -connect example.com:443 -tls1_2
openssl s_client -connect example.com:443 -tls1_3

# Zertifikatskette anzeigen
openssl s_client -connect example.com:443 -showcerts

# SNI (Server Name Indication)
openssl s_client -connect server.com:443 -servername www.example.com

Ablaufdatum prüfen:

# Website-Zertifikat
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | \
    openssl x509 -noout -enddate
# notAfter=Jan 15 23:59:59 2025 GMT

# Tage bis Ablauf
expiry=$(echo | openssl s_client -connect example.com:443 2>/dev/null | \
    openssl x509 -noout -enddate | cut -d= -f2)
echo $(( ($(date -jf "%b %d %H:%M:%S %Y %Z" "$expiry" +%s) - $(date +%s)) / 86400 )) Tage

Selbstsigniertes Zertifikat erstellen:

# Schlüssel und Zertifikat in einem Schritt
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes

# Mit spezifischen Angaben
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes \
    -subj "/CN=localhost/O=Development/C=DE"

2.2 Dateien verschlüsseln (symmetrisch)

Datei verschlüsseln:

# AES-256-CBC (empfohlen)
openssl enc -aes-256-cbc -salt -pbkdf2 -in datei.txt -out datei.enc

# Mit explizitem Passwort (unsicher für Skripte!)
openssl enc -aes-256-cbc -salt -pbkdf2 -in datei.txt -out datei.enc -pass pass:geheim

# Passwort aus Datei
openssl enc -aes-256-cbc -salt -pbkdf2 -in datei.txt -out datei.enc -pass file:password.txt

# Base64-kodiert (für E-Mail etc.)
openssl enc -aes-256-cbc -salt -pbkdf2 -a -in datei.txt -out datei.enc

Datei entschlüsseln:

# Standard
openssl enc -aes-256-cbc -d -pbkdf2 -in datei.enc -out datei.txt

# Base64-kodiert
openssl enc -aes-256-cbc -d -pbkdf2 -a -in datei.enc -out datei.txt

Verfügbare Algorithmen:

# Alle Cipher auflisten
openssl enc -list

# Empfohlene Algorithmen:
# aes-256-cbc    - AES 256-bit, CBC-Modus
# aes-256-gcm    - AES 256-bit, GCM-Modus (authentifiziert)
# chacha20       - ChaCha20 Stream-Cipher

Wichtige Optionen:

OptionBeschreibung
-eEncrypt (Standard)
-dDecrypt
-saltSalt verwenden (Standard, wichtig!)
-pbkdf2Moderne Key-Derivation (empfohlen)
-iter NPBKDF2-Iterationen
-aBase64-Encoding
-in FILEEingabedatei
-out FILEAusgabedatei
-passPasswort-Quelle

2.3 Hashes und Checksummen

Einzelne Datei hashen:

# SHA-256 (empfohlen)
openssl sha256 datei.txt
# SHA256(datei.txt)= abc123...

# Nur Hash (ohne Dateiname)
openssl sha256 -r datei.txt | cut -d' ' -f1

# Verschiedene Algorithmen
openssl md5 datei.txt        # veraltet
openssl sha1 datei.txt       # veraltet
openssl sha256 datei.txt     # empfohlen
openssl sha384 datei.txt
openssl sha512 datei.txt

Alternative: Standard-Tools:

# macOS eingebaute Tools
shasum -a 256 datei.txt
md5 datei.txt

# SHA-256
shasum -a 256 datei.txt
# abc123...  datei.txt

# SHA-512
shasum -a 512 datei.txt

Checksumme prüfen:

# Checksummen-Datei erstellen
shasum -a 256 *.zip > checksums.sha256

# Prüfen
shasum -a 256 -c checksums.sha256
# file1.zip: OK
# file2.zip: OK

HMAC (Hash mit Schlüssel):

# HMAC-SHA256
openssl dgst -sha256 -hmac "geheimer-schluessel" datei.txt

# Aus Datei
openssl dgst -sha256 -hmac "$(cat key.txt)" datei.txt

String hashen:

# String statt Datei
echo -n "zu hashender text" | openssl sha256
echo -n "zu hashender text" | shasum -a 256

# Wichtig: -n verhindert Newline am Ende!

Passwort-Hash erstellen:

# Für /etc/shadow-ähnliche Hashes
openssl passwd -6 "meinpasswort"    # SHA-512
openssl passwd -5 "meinpasswort"    # SHA-256
openssl passwd -1 "meinpasswort"    # MD5 (veraltet)

# Mit Salt
openssl passwd -6 -salt "randomsalt" "meinpasswort"

2.4 Zufallsdaten generieren

Zufällige Bytes:

# 32 Bytes als Hex
openssl rand -hex 32
# a1b2c3d4e5f6...

# 32 Bytes als Base64
openssl rand -base64 32
# QUJDREVGR0hJSktMTU5PUFE=

# In Datei
openssl rand -out random.bin 256

# Für Passwörter (URL-safe)
openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 32

Sichere Passwörter generieren:

# Zufälliges Passwort
openssl rand -base64 18
# kQ7xY9mN2pL4vR6t8wZa

# Alphanumerisch
openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c 16

# Mit Sonderzeichen
openssl rand -base64 32 | head -c 20

Alternative: macOS security:

# Zufälliges Passwort mit Keychain-Tool
security random-string 20

# UUID generieren
uuidgen

Für Kryptographie:

# AES-256 Schlüssel (32 Bytes)
openssl rand -hex 32

# IV für AES (16 Bytes)
openssl rand -hex 16

# Salt
openssl rand -hex 8

3 Sichere Administration

3.1 sudoedit – Dateien sicher bearbeiten

sudoedit (oder sudo -e) bearbeitet Dateien sicher, ohne den Editor als root zu starten.

Problem mit sudo vim:

# UNSICHER: vim läuft als root
sudo vim /etc/hosts
# vim-Plugins, Shell-Escapes etc. laufen alle als root!

Lösung mit sudoedit:

# SICHER: Editor läuft als normaler User
sudoedit /etc/hosts
# oder
sudo -e /etc/hosts

Wie es funktioniert:

  1. Erstellt temporäre Kopie der Datei
  2. Öffnet Editor als normaler Benutzer
  3. Kopiert Änderungen nach Schließen zurück (als root)

Editor konfigurieren:

# In ~/.zshrc
export SUDO_EDITOR="nano"
# oder
export SUDO_EDITOR="vim"
export SUDO_EDITOR="code --wait"

# Alternativ
export VISUAL="nano"
export EDITOR="nano"

Vorteile:

sudo vimsudoedit
Editor als rootEditor als User
Plugins als rootPlugins als User
Shell-Escape = rootShell-Escape = User
UnsicherSicher

3.2 /etc/sudoers und visudo

Die Datei /etc/sudoers kontrolliert sudo-Berechtigungen. Sie darf nur mit visudo bearbeitet werden.

visudo verwenden:

# Konfiguration bearbeiten (prüft Syntax!)
sudo visudo

# Mit spezifischem Editor
sudo EDITOR=nano visudo

# Syntax einer Datei prüfen
sudo visudo -c
sudo visudo -cf /etc/sudoers.d/custom

Warum visudo?

  • Prüft Syntax vor dem Speichern
  • Verhindert gleichzeitige Bearbeitung
  • Fehlerhafte Syntax = kein sudo mehr möglich!

sudoers-Syntax:

# Grundformat
WER  WO = (ALS_WER)  WAS

# Beispiele
root    ALL=(ALL:ALL) ALL
%admin  ALL=(ALL) ALL
max     ALL=(ALL) NOPASSWD: ALL

Felder erklärt:

FeldBedeutungBeispiel
WERBenutzer oder %gruppemax, %admin
WOHostsALL, localhost
ALS_WERZielbenutzer:gruppe(ALL), (root)
WASErlaubte BefehleALL, /usr/bin/apt

Praktische Beispiele:

# Benutzer max darf alles ohne Passwort
max ALL=(ALL) NOPASSWD: ALL

# Gruppe developers darf Docker ohne Passwort
%developers ALL=(ALL) NOPASSWD: /usr/bin/docker, /usr/bin/docker-compose

# Benutzer backup darf nur rsync als root
backup ALL=(root) NOPASSWD: /usr/bin/rsync

# Benutzer webadmin darf Webserver neustarten
webadmin ALL=(root) NOPASSWD: /usr/bin/systemctl restart nginx, /usr/bin/systemctl restart apache2

# Umgebungsvariablen erhalten
Defaults env_keep += "HOME"
Defaults env_keep += "SSH_AUTH_SOCK"

Include-Dateien:

# Separate Dateien in /etc/sudoers.d/
sudo visudo -f /etc/sudoers.d/developers

# In /etc/sudoers aktiviert durch:
@includedir /etc/sudoers.d
# oder (ältere Syntax)
#includedir /etc/sudoers.d

Aliase definieren:

# Benutzer-Aliase
User_Alias ADMINS = alice, bob, carol
User_Alias DEVELOPERS = dev1, dev2, dev3

# Befehls-Aliase
Cmnd_Alias SERVICES = /usr/bin/systemctl restart *, /usr/bin/systemctl status *
Cmnd_Alias DOCKER = /usr/bin/docker, /usr/bin/docker-compose
Cmnd_Alias NETWORKING = /sbin/ifconfig, /sbin/route, /usr/bin/ping

# Verwendung
ADMINS ALL=(ALL) ALL
DEVELOPERS ALL=(root) NOPASSWD: DOCKER, SERVICES

3.3 Einschränkungen mit sudo

Befehle einschränken:

# Nur bestimmte Befehle
max ALL=(root) /usr/bin/apt update, /usr/bin/apt upgrade

# Mit Wildcards
max ALL=(root) /usr/bin/systemctl restart nginx*

# Ohne Argumente
max ALL=(root) /usr/bin/reboot ""

# Bestimmte Argumente verbieten
max ALL=(root) /usr/bin/rm, !/usr/bin/rm -rf /

NOPASSWD vs PASSWD:

# Ohne Passwort
max ALL=(ALL) NOPASSWD: /usr/bin/docker

# Mit Passwort (Standard)
max ALL=(ALL) PASSWD: /usr/bin/visudo

# Gemischt
max ALL=(ALL) PASSWD: ALL, NOPASSWD: /usr/bin/docker

Verbote (!):

# Alles außer bestimmte Befehle
max ALL=(ALL) ALL, !/bin/su, !/bin/bash, !/usr/bin/passwd root

# Shell-Escape verhindern
max ALL=(ALL) NOEXEC: /usr/bin/vim

NOEXEC – Shell-Escapes verhindern:

# Verhindert dass vim Shell startet
max ALL=(root) NOEXEC: /usr/bin/vim

Logging:

# Alle Befehle loggen
Defaults log_input, log_output
Defaults logfile="/var/log/sudo.log"

# I/O-Logging
Defaults iolog_dir="/var/log/sudo-io"

Timeout-Einstellungen:

# Passwort-Timeout (Minuten)
Defaults timestamp_timeout=15

# Sofort Passwort verlangen
Defaults timestamp_timeout=0

# Nie wieder fragen (Session)
Defaults timestamp_timeout=-1

# Anzahl Passwort-Versuche
Defaults passwd_tries=3

Sicherheits-Empfehlungen:

# Sichere PATH-Einstellung
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

# Root-Passwort bei Authentifizierung verlangen
Defaults rootpw

# TTY erforderlich (verhindert Skript-Missbrauch)
Defaults requiretty

# Insults bei falschem Passwort (Spaß)
Defaults insults

Sudo-Berechtigungen prüfen:

# Eigene Berechtigungen anzeigen
sudo -l

# Ausgabe:
# User max may run the following commands on hostname:
#     (ALL) ALL
#     (root) NOPASSWD: /usr/bin/docker

# Für anderen Benutzer
sudo -l -U alice

Sudo-Session beenden:

# Timestamp zurücksetzen (Passwort wird wieder verlangt)
sudo -k

# Timestamp komplett löschen
sudo -K

Best Practices:

  1. Minimal notwendige Rechte: Nur spezifische Befehle erlauben
  2. Keine Shell-Befehle: /bin/bash, /bin/su vermeiden
  3. NOEXEC nutzen: Bei Editoren und Programmen mit Shell-Escape
  4. Separate Dateien: /etc/sudoers.d/ für Organisation
  5. Logging aktivieren: Für Audit-Trail
  6. Regelmäßig prüfen: sudo -l und visudo -c

Cheatsheet sudoers:

# Format
USER HOST=(RUNAS) COMMAND

# Beispiele
root        ALL=(ALL:ALL) ALL           # root darf alles
%admin      ALL=(ALL) ALL               # Gruppe admin darf alles
max         ALL=(ALL) NOPASSWD: ALL     # max ohne Passwort
deploy      ALL=(root) /usr/bin/docker  # deploy nur docker
backup      ALL=(root) NOPASSWD: /usr/bin/rsync  # backup rsync ohne PW

# Verbote
max         ALL=(ALL) ALL, !/bin/su     # alles außer su

# Aliase
User_Alias DEVS = alice, bob
Cmnd_Alias DOCKER = /usr/bin/docker
DEVS        ALL=(root) NOPASSWD: DOCKER

# Sicherheit
Defaults    requiretty
Defaults    timestamp_timeout=5
Defaults    secure_path="..."

macOS-Integration

macOS bietet viele Terminal-Befehle zur Integration mit dem Betriebssystem: Zwischenablage, Finder, Systemeinstellungen und mehr.

1 Zwischenablage

Die macOS-Zwischenablage (Pasteboard) ist vollständig vom Terminal aus zugänglich.

1.1 pbcopy – In Zwischenablage kopieren

pbcopy kopiert stdin in die Zwischenablage.

Grundlegende Nutzung:

# Text kopieren
echo "Hallo Welt" | pbcopy

# Dateiinhalt kopieren
cat datei.txt | pbcopy
pbcopy < datei.txt

# Befehlsausgabe kopieren
pwd | pbcopy
ls -la | pbcopy

# Mehrere Zeilen
cat << EOF | pbcopy
Zeile 1
Zeile 2
Zeile 3
EOF

Optionen:

# Standard-Pasteboard (wie Cmd+C)
echo "text" | pbcopy

# Find-Pasteboard (für Cmd+G Suche)
echo "suchtext" | pbcopy -pboard find

# Ruler-Pasteboard
echo "data" | pbcopy -pboard ruler

1.2 pbpaste – Aus Zwischenablage einfügen

pbpaste gibt den Inhalt der Zwischenablage auf stdout aus.

Grundlegende Nutzung:

# In Terminal ausgeben
pbpaste

# In Datei speichern
pbpaste > datei.txt

# An Datei anhängen
pbpaste >> datei.txt

# Als Variable
content=$(pbpaste)
echo "$content"

Optionen:

# Standard-Pasteboard
pbpaste

# Find-Pasteboard
pbpaste -pboard find

# Nur Plain Text (falls Rich Text kopiert)
pbpaste -Prefer txt

1.3 Praxisbeispiele mit Pipes

Textverarbeitung:

# Kopierte URLs bereinigen
pbpaste | tr -d '\n' | pbcopy

# Großbuchstaben
pbpaste | tr '[:lower:]' '[:upper:]' | pbcopy

# Zeilen sortieren
pbpaste | sort | pbcopy

# Duplikate entfernen
pbpaste | sort -u | pbcopy

# Leerzeichen trimmen
pbpaste | sed 's/^[:space:](:space:.md)*//;s/[:space:](:space:.md)*$//' | pbcopy

# Zeilen nummerieren
pbpaste | nl | pbcopy

Entwicklung:

# Git diff kopieren
git diff | pbcopy

# JSON formatieren
pbpaste | jq . | pbcopy

# Base64 kodieren
pbpaste | base64 | pbcopy

# Base64 dekodieren
pbpaste | base64 -d | pbcopy

# MD5-Hash von Clipboard-Inhalt
pbpaste | md5

# URL-Encoding
pbpaste | python3 -c "import sys, urllib.parse; print(urllib.parse.quote(sys.stdin.read().strip()))" | pbcopy

Pfade und Dateien:

# Aktuellen Pfad kopieren
pwd | pbcopy

# Dateiliste kopieren
ls -1 | pbcopy

# Voller Pfad einer Datei
realpath datei.txt | pbcopy

# SSH Public Key kopieren
cat ~/.ssh/id_ed25519.pub | pbcopy

Nützliche Aliase:

# ~/.zshrc

# Clipboard-Shortcuts
alias cb='pbcopy'
alias cbp='pbpaste'
alias cbpwd='pwd | pbcopy'

# Pfad kopieren
cppath() { echo -n "$(pwd)/$1" | pbcopy; echo "Copied: $(pwd)/$1"; }

# Dateiinhalt kopieren
cpfile() { cat "$1" | pbcopy; echo "Copied contents of $1"; }

# JSON formatieren und kopieren
cbjson() { pbpaste | jq . | pbcopy; echo "JSON formatted"; }

# Clipboard-Inhalt anzeigen und in Datei speichern
cbsave() { pbpaste | tee "$1"; }

2 Dateien & Programme öffnen

2.1 open – Dateien und URLs öffnen

open öffnet Dateien, Verzeichnisse und URLs mit der Standardanwendung.

Dateien öffnen:

# Mit Standard-App öffnen
open dokument.pdf
open bild.png
open tabelle.xlsx

# Mehrere Dateien
open datei1.txt datei2.txt datei3.txt

# Alle Bilder im Ordner
open *.jpg

Verzeichnisse öffnen:

# Aktuelles Verzeichnis im Finder
open .

# Bestimmtes Verzeichnis
open ~/Documents
open /Applications

# Home-Verzeichnis
open ~

URLs öffnen:

# Webseite im Standard-Browser
open https://www.example.com

# E-Mail-Link
open mailto:max@example.com

# FTP
open ftp://ftp.example.com

# Mit Parametern
open "https://www.google.com/search?q=terminal+commands"

Spezielle URLs:

# Systemeinstellungen
open "x-apple.systempreferences:"
open "x-apple.systempreferences:com.apple.preference.security"

# App Store
open "macappstore://itunes.apple.com/app/id1234567890"

# Maps
open "maps://?q=Berlin"

# FaceTime
open "facetime://max@example.com"

2.2 open -a – Mit bestimmter App öffnen

# Mit spezifischer App öffnen
open -a "Visual Studio Code" datei.txt
open -a Safari https://example.com
open -a "Google Chrome" index.html
open -a Preview bild.png
open -a TextEdit dokument.txt

# App ohne Datei starten
open -a Finder
open -a Terminal
open -a "Activity Monitor"

# Kurzform für .app im Applications-Ordner
open -a Safari

Mit Bundle-ID:

# Über Bundle-Identifier
open -b com.apple.Safari https://example.com
open -b com.microsoft.VSCode datei.txt
open -b com.apple.TextEdit dokument.txt

# Bundle-ID einer App finden
osascript -e 'id of app "Safari"'
# com.apple.Safari

mdls -name kMDItemCFBundleIdentifier /Applications/Safari.app

Neue Instanz:

# Neue App-Instanz (zweites Fenster)
open -n -a Safari

# Versteckt starten
open -j -a "Some App"

# Im Hintergrund (nicht in Vordergrund bringen)
open -g datei.txt

2.3 open -R – Im Finder anzeigen

# Datei im Finder zeigen (Reveal)
open -R datei.txt

# Öffnet Finder mit selektierter Datei
open -R ~/Documents/wichtig.pdf

# Nützlich für Downloads
open -R ~/Downloads/installer.dmg

Weitere Optionen:

OptionBeschreibung
-a APPMit bestimmter App öffnen
-b BUNDLEMit Bundle-ID öffnen
-eMit TextEdit öffnen
-tMit Standard-Texteditor öffnen
-fstdin als Datei öffnen
-FFrische App (ohne gespeicherten Zustand)
-WWarten bis App beendet
-RIm Finder anzeigen
-nNeue Instanz
-gIm Hintergrund
-jVersteckt starten
-hApp verstecken wenn andere aktiv

Praktische Beispiele:

# Code im Editor öffnen
open -a "Visual Studio Code" ~/projekte/app/

# Screenshot im Preview öffnen
screencapture -i /tmp/screenshot.png && open -a Preview /tmp/screenshot.png

# Befehlsausgabe im Editor öffnen
ls -la | open -f -a TextEdit

# Auf App-Ende warten
open -W dokument.pdf
echo "PDF wurde geschlossen"

# Quick Look (Vorschau)
qlmanage -p datei.pdf

3 Systemeinstellungen

3.1 defaults – Einstellungen lesen

defaults liest und schreibt macOS-Einstellungen (Property Lists).

Einstellungen lesen:

# Alle Einstellungen einer App
defaults read com.apple.finder

# Bestimmte Einstellung
defaults read com.apple.finder ShowPathbar

# Globale Einstellungen
defaults read NSGlobalDomain

# Bestimmte globale Einstellung
defaults read NSGlobalDomain AppleShowAllExtensions

Einstellungen suchen:

# Nach Schlüssel suchen
defaults read | grep -i "pathbar"

# Alle Domains auflisten
defaults domains | tr ',' '\n'

# Alle Einstellungen einer Domain durchsuchen
defaults read com.apple.dock | grep -i "size"

Typ einer Einstellung:

# Typ ermitteln
defaults read-type com.apple.finder ShowStatusBar
# Type is boolean

3.2 defaults write – Einstellungen ändern

Syntax:

defaults write DOMAIN KEY VALUE
defaults write DOMAIN KEY -TYPE VALUE

Typen:

TypFlagBeispiel
String-string"text"
Integer-int42
Float-float3.14
Boolean-booltrue, false, YES, NO
Array-arrayitem1 item2
Dictionary-dictkey1 value1 key2 value2

Beispiele:

# Boolean
defaults write com.apple.finder ShowPathbar -bool true

# Integer
defaults write com.apple.dock tilesize -int 48

# Float
defaults write NSGlobalDomain com.apple.mouse.scaling -float 2.5

# String
defaults write com.apple.screencapture location -string "~/Screenshots"

# Array
defaults write com.apple.dock persistent-apps -array

Nach Änderungen:

# Finder neu starten
killall Finder

# Dock neu starten
killall Dock

# SystemUIServer neu starten (Menüleiste)
killall SystemUIServer

3.3 Nützliche versteckte Einstellungen

Finder:

# Versteckte Dateien anzeigen
defaults write com.apple.finder AppleShowAllFiles -bool true

# Pfadleiste anzeigen
defaults write com.apple.finder ShowPathbar -bool true

# Statusleiste anzeigen
defaults write com.apple.finder ShowStatusBar -bool true

# Dateiendungen immer anzeigen
defaults write NSGlobalDomain AppleShowAllExtensions -bool true

# Warnung bei Endungsänderung deaktivieren
defaults write com.apple.finder FXEnableExtensionChangeWarning -bool false

# Standard-Ansicht (Liste)
defaults write com.apple.finder FXPreferredViewStyle -string "Nlsv"
# Nlsv=Liste, icnv=Icons, clmv=Spalten, Flwv=Cover Flow

# Neues Fenster zeigt Home
defaults write com.apple.finder NewWindowTarget -string "PfHm"

# Finder beenden erlauben
defaults write com.apple.finder QuitMenuItem -bool true

killall Finder

Dock:

# Dock-Größe
defaults write com.apple.dock tilesize -int 36

# Vergrößerung
defaults write com.apple.dock magnification -bool true
defaults write com.apple.dock largesize -int 64

# Position (left, bottom, right)
defaults write com.apple.dock orientation -string "left"

# Automatisch ausblenden
defaults write com.apple.dock autohide -bool true

# Ausblende-Verzögerung
defaults write com.apple.dock autohide-delay -float 0
defaults write com.apple.dock autohide-time-modifier -float 0.5

# Nur aktive Apps zeigen
defaults write com.apple.dock static-only -bool true

# Letzte Apps deaktivieren
defaults write com.apple.dock show-recents -bool false

# App-Indikatoren (Punkte) ausblenden
defaults write com.apple.dock show-process-indicators -bool false

# Spaces-Animation beschleunigen
defaults write com.apple.dock workspaces-edge-delay -float 0.1

killall Dock

Screenshots:

# Speicherort ändern
defaults write com.apple.screencapture location -string "~/Screenshots"

# Format ändern (png, jpg, gif, pdf, tiff)
defaults write com.apple.screencapture type -string "png"

# Schatten deaktivieren
defaults write com.apple.screencapture disable-shadow -bool true

# Dateiname-Präfix
defaults write com.apple.screencapture name -string "Screenshot"

# Zeitstempel im Namen deaktivieren
defaults write com.apple.screencapture include-date -bool false

killall SystemUIServer

Safari:

# Entwicklermenü aktivieren
defaults write com.apple.Safari IncludeDevelopMenu -bool true

# Web Inspector aktivieren
defaults write com.apple.Safari WebKitDeveloperExtrasEnabledPreferenceKey -bool true

# Vollständige URL anzeigen
defaults write com.apple.Safari ShowFullURLInSmartSearchField -bool true

# Links in Tabs öffnen
defaults write com.apple.Safari TargetedClicksCreateTabs -bool true

Tastatur & Eingabe:

# Tastenwiederholung (schneller)
defaults write NSGlobalDomain KeyRepeat -int 2

# Verzögerung bis Wiederholung
defaults write NSGlobalDomain InitialKeyRepeat -int 15

# Autokorrektur deaktivieren
defaults write NSGlobalDomain NSAutomaticSpellingCorrectionEnabled -bool false

# Automatische Großschreibung deaktivieren
defaults write NSGlobalDomain NSAutomaticCapitalizationEnabled -bool false

# Smart Quotes deaktivieren
defaults write NSGlobalDomain NSAutomaticQuoteSubstitutionEnabled -bool false

# Smart Dashes deaktivieren
defaults write NSGlobalDomain NSAutomaticDashSubstitutionEnabled -bool false

Verschiedenes:

# Crash-Reporter-Dialoge als Notification
defaults write com.apple.CrashReporter UseUNC 1

# Erweiterten Druckdialog als Standard
defaults write NSGlobalDomain PMPrintingExpandedStateForPrint -bool true

# Erweiterten Speicherdialog als Standard
defaults write NSGlobalDomain NSNavPanelExpandedStateForSaveMode -bool true

# .DS_Store auf Netzwerklaufwerken deaktivieren
defaults write com.apple.desktopservices DSDontWriteNetworkStores -bool true

# Bluetooth-Audio-Qualität (aptX)
defaults write com.apple.BluetoothAudioAgent "Apple Bitpool Min (editable)" -int 40

# Subpixel-Font-Rendering (für externe Monitore)
defaults write NSGlobalDomain AppleFontSmoothing -int 1

# App Nap deaktivieren (für alle Apps)
defaults write NSGlobalDomain NSAppSleepDisabled -bool true

Mission Control:

# Fenster nach App gruppieren
defaults write com.apple.dock expose-group-apps -bool true

# Spaces automatisch neu anordnen deaktivieren
defaults write com.apple.dock mru-spaces -bool false

# Dashboard deaktivieren
defaults write com.apple.dashboard mcx-disabled -bool true

killall Dock

3.4 defaults delete – Einstellungen zurücksetzen

# Einzelne Einstellung löschen
defaults delete com.apple.finder ShowPathbar

# Alle Einstellungen einer App zurücksetzen
defaults delete com.apple.finder

# Globale Einstellung löschen
defaults delete NSGlobalDomain AppleShowAllExtensions

# Nach Löschen: App neu starten
killall Finder

Aktuelle Einstellungen sichern:

# Domain exportieren
defaults export com.apple.finder ~/finder-backup.plist

# Domain importieren
defaults import com.apple.finder ~/finder-backup.plist

4 Automator & Shortcuts

4.1 Shell-Skripte in Automator

Automator kann Shell-Skripte ausführen und in Workflows integrieren.

Workflow erstellen:

  1. Automator öffnen
  2. Neues Dokument → Workflow oder Quick Action
  3. “Shell-Skript ausführen” aus der Bibliothek ziehen
  4. Shell wählen (/bin/zsh) und Skript eingeben

Eingabe-Optionen:

OptionBeschreibung
“an stdin”Eingabe als stdin
“als Argumente”Eingabe als $1, $2, …

Beispiel: Bilder konvertieren

#!/bin/zsh
# Shell: /bin/zsh
# Eingabe: als Argumente

for f in "$@"; do
    sips -s format png "$f" --out "${f%.*}.png"
done

Beispiel: Text transformieren

#!/bin/zsh
# Shell: /bin/zsh
# Eingabe: an stdin

tr '[:lower:]' '[:upper:]'

Beispiel: Clipboard verarbeiten

#!/bin/zsh
# Kein Input nötig

pbpaste | sort -u | pbcopy
osascript -e 'display notification "Duplikate entfernt" with title "Automator"'

4.2 Quick Actions erstellen

Quick Actions erscheinen im Finder-Kontextmenü und der Touch Bar.

Quick Action erstellen:

  1. Automator → Neues Dokument → “Schnellaktion”
  2. Eingabe: “Dateien oder Ordner” in “Finder”
  3. Shell-Skript hinzufügen
  4. Speichern (Name erscheint im Kontextmenü)

Beispiel: In Terminal öffnen

#!/bin/zsh
# Eingabe: als Argumente

for f in "$@"; do
    if [-d "$f"](-d%20"$f".md); then
        open -a Terminal "$f"
        break
    else
        open -a Terminal "$(dirname "$f")"
        break
    fi
done

Beispiel: Bilder verkleinern

#!/bin/zsh
# Eingabe: als Argumente

for f in "$@"; do
    sips --resampleWidth 1920 "$f" --out "${f%.*}_small.${f##*.}"
done

osascript -e 'display notification "Bilder verkleinert" with title "Quick Action"'

Beispiel: PDF zusammenfügen

#!/bin/zsh
# Eingabe: als Argumente

output="$HOME/Desktop/Merged_$(date +%Y%m%d_%H%M%S).pdf"
"/System/Library/Automator/Combine PDF Pages.action/Contents/MacOS/join" -o "$output" "$@"
open -R "$output"

Speicherort:

~/Library/Services/

4.3 Integration mit Shortcuts-App

Die Shortcuts-App (ab macOS Monterey) kann Shell-Skripte ausführen.

Shell-Skript in Shortcuts:

  1. Shortcuts-App öffnen
  2. Neuer Shortcut
  3. “Shell-Skript ausführen” hinzufügen
  4. Shell und Eingabe konfigurieren

Eingabe verwenden:

#!/bin/zsh
# "Shortcut-Eingabe" ist verfügbar als stdin oder $1

# Als stdin
cat | tr '[:lower:]' '[:upper:]'

# Als Argument
echo "$1" | tr '[:lower:]' '[:upper:]'

Shortcuts per Terminal ausführen:

# Shortcut ausführen
shortcuts run "Mein Shortcut"

# Mit Eingabe
echo "text" | shortcuts run "Mein Shortcut"

# Ergebnis erhalten
shortcuts run "Mein Shortcut" <<< "input"

# Shortcut-Liste
shortcuts list

Beispiel: API-Aufruf

#!/bin/zsh
curl -s "https://api.example.com/data" | jq -r '.result'

Beispiel: Systeminfo

#!/bin/zsh
echo "Hostname: $(hostname)"
echo "User: $(whoami)"
echo "Uptime: $(uptime | awk -F'up ' '{print $2}' | awk -F',' '{print $1}')"
echo "IP: $(ipconfig getifaddr en0)"

5 Weitere macOS-Befehle

5.1 say – Text vorlesen

say wandelt Text in Sprache um (Text-to-Speech).

Grundlegende Nutzung:

# Text vorlesen
say "Hallo Welt"

# Aus Datei vorlesen
say -f dokument.txt

# Von stdin
echo "Test" | say

# Clipboard vorlesen
pbpaste | say

Stimme wählen:

# Mit bestimmter Stimme
say -v Anna "Guten Tag"       # Deutsch
say -v Samantha "Hello"       # Englisch (US)
say -v Daniel "Hello"         # Englisch (UK)

# Verfügbare Stimmen
say -v ?

# Alle deutschen Stimmen
say -v ? | grep de_

In Datei speichern:

# Als AIFF
say -o ausgabe.aiff "Dieser Text wird gespeichert"

# Als MP4/M4A
say -o ausgabe.m4a --data-format=aac "Dieser Text"

Geschwindigkeit:

# Langsamer (Standard: ~175 Wörter/Minute)
say -r 100 "Langsam gesprochen"

# Schneller
say -r 250 "Schnell gesprochen"

Interaktiv (Zeichen für Zeichen):

# Eingabe Zeichen für Zeichen vorlesen
say -i

Praktische Anwendungen:

# Nach langem Befehl benachrichtigen
make build && say "Build fertig" || say "Build fehlgeschlagen"

# Timer
sleep 300 && say "5 Minuten vorbei"

# Pomodoro
{ sleep 1500 && say "Pause machen"; sleep 300 && say "Weiterarbeiten"; } &

5.2 screencapture – Screenshots per Terminal

Bildschirmfoto:

# Ganzer Bildschirm
screencapture screenshot.png

# Interaktive Auswahl (wie Cmd+Shift+4)
screencapture -i screenshot.png

# Bestimmtes Fenster (wie Cmd+Shift+4, Space)
screencapture -iW screenshot.png

# Mit Mauszeiger
screencapture -C screenshot.png

# Ohne Schatten bei Fenstern
screencapture -o screenshot.png

In Zwischenablage:

# In Clipboard statt Datei
screencapture -c

# Interaktiv in Clipboard
screencapture -ic

Verzögerung:

# 5 Sekunden warten
screencapture -T 5 screenshot.png

# Interaktiv mit Verzögerung
screencapture -i -T 3 screenshot.png

Formate:

# PNG (Standard)
screencapture -t png screenshot.png

# JPEG
screencapture -t jpg screenshot.jpg

# PDF
screencapture -t pdf screenshot.pdf

# TIFF
screencapture -t tiff screenshot.tiff

Mehrere Bildschirme:

# Jeden Monitor separat
screencapture screen1.png screen2.png

# Nur bestimmten Monitor (-D)
screencapture -D 1 screen.png   # Hauptmonitor
screencapture -D 2 screen.png   # Zweiter Monitor

Optionen:

OptionBeschreibung
-iInteraktive Auswahl
-iWFenster-Auswahl
-iUFenster ohne Schatten
-cIn Zwischenablage
-CMit Mauszeiger
-oOhne Fensterschatten
-T SECVerzögerung
-t TYPEFormat (png, jpg, pdf)
-xOhne Ton
-D NDisplay N
-R x,y,w,hRechteck-Bereich

Praktische Beispiele:

# Schneller Screenshot auf Desktop
screencapture -x ~/Desktop/screenshot-$(date +%Y%m%d-%H%M%S).png

# Bereich 800x600 ab Position 100,100
screencapture -R 100,100,800,600 region.png

# Screenshot-Funktion
screenshot() {
    local name="${1:-screenshot}"
    screencapture -i ~/Screenshots/"${name}_$(date +%Y%m%d_%H%M%S).png"
}

5.3 caffeinate – Ruhezustand verhindern

caffeinate verhindert, dass der Mac in den Ruhezustand geht.

Grundlegende Nutzung:

# Bis zum Abbruch (Ctrl+C)
caffeinate

# Für bestimmte Zeit (Sekunden)
caffeinate -t 3600   # 1 Stunde

# Während Befehl läuft
caffeinate -s make build

# Während Prozess läuft
caffeinate -w 1234   # PID 1234

Optionen:

OptionBeschreibung
-dDisplay wach halten
-iSystem wach halten (idle)
-mDisk wach halten
-sSystem wach halten (auch mit Netzteil)
-uUser aktiv simulieren
-t SECTimeout in Sekunden
-w PIDBis Prozess endet

Praktische Beispiele:

# Während langer Download
caffeinate -i wget https://example.com/large-file.zip

# Während Backup
caffeinate -im rsync -avz /source/ /backup/

# Präsentation: Display an lassen
caffeinate -d -t 7200  # 2 Stunden

# Bis bestimmter Prozess endet
caffeinate -w $(pgrep -x "Handbrake")

Als Hintergrundprozess:

# Im Hintergrund starten
caffeinate -d &
CAFFEINE_PID=$!

# Später beenden
kill $CAFFEINE_PID

5.4 mdfind – Spotlight-Suche per Terminal

mdfind nutzt den Spotlight-Index für schnelle Suchen.

Grundlegende Suche:

# Nach Name oder Inhalt
mdfind "projektplan"

# Nur Dateinamen
mdfind -name "readme.md"

# In bestimmtem Verzeichnis
mdfind -onlyin ~/Documents "bericht"

Nach Dateityp:

# PDFs
mdfind "kind:pdf projektplan"

# Bilder
mdfind "kind:image"

# E-Mails
mdfind "kind:email from:max"

# Präsentationen
mdfind "kind:presentation"

Metadata-Attribute:

# Nach Erstellungsdatum
mdfind "kMDItemContentCreationDate > \$time.today(-7)"

# Nach Autor
mdfind "kMDItemAuthors == 'Max Mustermann'"

# Nach Dateiendung
mdfind "kMDItemFSName == '*.py'"

# Nach Größe (Bytes)
mdfind "kMDItemFSSize > 100000000"  # > 100 MB

Kombinierte Abfragen:

# PDF und Name
mdfind "kind:pdf AND name:report"

# In Ordner, bestimmter Typ
mdfind -onlyin ~/Projects "kind:source"

# Kürzlich geändert
mdfind "kMDItemContentModificationDate > \$time.today(-1)"

Live-Suche:

# Ergebnisse live aktualisieren
mdfind -live "projektplan"

Mit anderen Befehlen:

# Gefundene PDFs öffnen
mdfind -name "report.pdf" | head -1 | xargs open

# Ergebnisse zählen
mdfind "kind:image" -onlyin ~/Pictures | wc -l

# Größe aller gefundenen Dateien
mdfind "kind:movie" | xargs du -ch | tail -1

Spotlight-Index verwalten:

# Index-Status
mdutil -s /

# Index neu erstellen
sudo mdutil -E /

# Spotlight für Volume deaktivieren
sudo mdutil -i off /Volumes/USB

5.5 afplay – Audio abspielen

afplay spielt Audiodateien über das Terminal.

Grundlegende Nutzung:

# Datei abspielen
afplay musik.mp3
afplay sound.aiff
afplay audio.m4a

# System-Sounds abspielen
afplay /System/Library/Sounds/Ping.aiff
afplay /System/Library/Sounds/Glass.aiff

Optionen:

# Lautstärke (0.0 - 1.0)
afplay -v 0.5 musik.mp3

# Abspielrate (1.0 = normal)
afplay -r 1.5 musik.mp3   # 50% schneller
afplay -r 0.5 musik.mp3   # 50% langsamer

# Bestimmte Zeit abspielen
afplay -t 10 musik.mp3    # 10 Sekunden

# Im Hintergrund
afplay musik.mp3 &

System-Sounds:

# Verfügbare System-Sounds
ls /System/Library/Sounds/

# Notification-Sound
afplay /System/Library/Sounds/Funk.aiff

# Für Skript-Benachrichtigungen
notify() {
    afplay /System/Library/Sounds/Glass.aiff
    osascript -e "display notification \"$1\" with title \"Terminal\""
}

# Verwendung
make build && notify "Build fertig"

Praktische Beispiele:

# Wecker
sleep 3600 && afplay /System/Library/Sounds/Ping.aiff

# Befehl mit Sound bei Fertigstellung
long-running-command; afplay /System/Library/Sounds/Hero.aiff

# Alle MP3s in Ordner abspielen
for f in *.mp3; do afplay "$f"; done

# Zufälliger System-Sound
afplay /System/Library/Sounds/$(ls /System/Library/Sounds/ | sort -R | head -1)

Aliase:

# ~/.zshrc

# Benachrichtigungs-Sounds
alias beep='afplay /System/Library/Sounds/Ping.aiff'
alias done='afplay /System/Library/Sounds/Glass.aiff'
alias error='afplay /System/Library/Sounds/Basso.aiff'

# Nach langem Befehl
alias alert='afplay /System/Library/Sounds/Hero.aiff && osascript -e "display notification \"Fertig\" with title \"Terminal\""'

Weitere Audio-Befehle:

# Systemlautstärke
osascript -e "set volume output volume 50"    # 0-100
osascript -e "output volume of (get volume settings)"

# Stummschalten
osascript -e "set volume output muted true"
osascript -e "set volume output muted false"

# Audio-Gerät Info
system_profiler SPAudioDataType

Python

python

BefehlBeschreibung
python --versionPython-Version prüfen
python -c "print('Hello')"Python-Code direkt ausführen
python script.pySkript ausführen
python -m module_nameModul als Skript ausführen
python -m venv .venvVirtuelle Umgebung erstellen
python -i script.pySkript ausführen, danach interaktiv bleiben
python -m http.server 8000Einfachen Webserver starten
python -m json.tool file.jsonJSON formatiert ausgeben

conda

BefehlBeschreibung
conda --versionConda-Version prüfen
conda env listUmgebungen auflisten
conda activate py310Umgebung aktivieren
conda deactivateUmgebung deaktivieren
conda create -n py310 python=3.10Umgebung erstellen
conda create --name py312 --clone py310Umgebung klonen
conda remove -n py310 --allUmgebung löschen
conda install numpyPaket installieren
conda install python=3.12Python-Version aktualisieren
conda update --allAlle Pakete aktualisieren
conda update condaConda selbst aktualisieren
conda listInstallierte Pakete anzeigen
conda search numpyNach Paketen suchen
conda infoConda-Informationen anzeigen
conda clean --allCache und ungenutzte Pakete löschen
conda env export > environment.ymlUmgebung exportieren
conda env create -f environment.ymlUmgebung aus Datei erstellen

pip

BefehlBeschreibung
pip --versionpip-Version prüfen
pip install packagePaket installieren
pip install package==1.2.3Bestimmte Version installieren
pip install -U packagePaket aktualisieren
pip install -r requirements.txtAlle Pakete aus Datei installieren
pip uninstall packagePaket deinstallieren
pip listInstallierte Pakete anzeigen
pip list --outdatedVeraltete Pakete anzeigen
pip show packagePaket-Informationen anzeigen
pip search packageNach Paketen suchen (oft deaktiviert)
pip freeze > requirements.txtInstallierte Pakete exportieren
pip cache purgepip-Cache leeren
pip install -e .Paket im Entwicklungsmodus installieren
pip install --user packageNur für aktuellen Benutzer installieren

Rust

rustup

BefehlBeschreibung
rustup --versionrustup-Version prüfen
rustup showInstallierte Toolchains anzeigen
rustup updateRust aktualisieren
rustup default stableStandard-Toolchain setzen
rustup default nightlyNightly als Standard setzen
rustup toolchain listInstallierte Toolchains auflisten
rustup toolchain install nightlyNightly installieren
rustup toolchain uninstall nightlyToolchain entfernen
rustup component add clippyKomponente hinzufügen
rustup component add rustfmtFormatter hinzufügen
rustup component listVerfügbare Komponenten anzeigen
rustup docRust-Dokumentation öffnen
rustup doc --stdStandardbibliothek-Doku öffnen
rustup override set nightlyToolchain für Projekt setzen
rustup self updaterustup selbst aktualisieren

cargo

Projekt erstellen & verwalten

BefehlBeschreibung
cargo new project_nameNeues Projekt erstellen (mit Git)
cargo new --lib library_nameNeue Bibliothek erstellen
cargo initProjekt im aktuellen Ordner initialisieren
cargo init --libBibliothek im aktuellen Ordner initialisieren

Bauen & Ausführen

BefehlBeschreibung
cargo buildProjekt kompilieren (Debug)
cargo build --releaseRelease-Build erstellen
cargo runKompilieren und ausführen
cargo run --releaseRelease-Build ausführen
cargo run -- arg1 arg2Mit Argumenten ausführen
cargo run --bin binary_nameBestimmte Binary ausführen
cargo run --example example_nameBeispiel ausführen

Testen & Prüfen

BefehlBeschreibung
cargo testAlle Tests ausführen
cargo test test_nameBestimmten Test ausführen
cargo test --releaseTests im Release-Modus
cargo test -- --nocaptureAusgabe nicht unterdrücken
cargo benchBenchmarks ausführen
cargo checkKompilierbarkeit prüfen (schnell)

Dependencies

BefehlBeschreibung
cargo add packageDependency hinzufügen
cargo add package@1.2.3Bestimmte Version hinzufügen
cargo add package --devDev-Dependency hinzufügen
cargo remove packageDependency entfernen
cargo updateDependencies aktualisieren
cargo treeDependency-Baum anzeigen
cargo search packageNach Crates suchen

Code-Qualität

BefehlBeschreibung
cargo fmtCode formatieren
cargo fmt --checkFormatierung prüfen
cargo clippyLinter ausführen
cargo clippy --fixLint-Fehler automatisch beheben
cargo fixCompiler-Warnungen beheben

Dokumentation & Infos

BefehlBeschreibung
cargo docDokumentation generieren
cargo doc --openDokumentation generieren und öffnen
cargo doc --no-depsOhne Dependencies dokumentieren

Aufräumen & Sonstiges

BefehlBeschreibung
cargo cleanBuild-Artefakte löschen
cargo publishCrate auf crates.io veröffentlichen
cargo install packageBinary global installieren
cargo uninstall packageBinary deinstallieren

make

BefehlBeschreibung
makeStandard-Target ausführen (meist all)
make targetBestimmtes Target ausführen
make -n targetDry-run – zeigt was ausgeführt würde
make -j4Parallel mit 4 Jobs bauen
make -j$(nproc)Parallel mit allen CPUs bauen
make -f other.mkAnderes Makefile verwenden
make -BAlles neu bauen (ignore timestamps)
make cleanAufräumen (falls Target definiert)
make -pAlle Variablen und Regeln anzeigen
make VAR=valueVariable beim Aufruf setzen

just

BefehlBeschreibung
justStandard-Rezept ausführen
just recipeBestimmtes Rezept ausführen
just -l / just --listAlle Rezepte auflisten
just --show recipeRezept-Definition anzeigen
just -n recipeDry-run – zeigt was ausgeführt würde
just --chooseRezept interaktiv auswählen (mit fzf)
just -f other.justAnderes Justfile verwenden
just --summaryKurze Übersicht aller Rezepte
just --evaluateAlle Variablen ausgeben
just recipe arg1 arg2Rezept mit Argumenten aufrufen
just --initNeues Justfile erstellen

Beispiel justfile:

# Standard-Rezept
default:
    @just --list

# Projekt bauen
build:
    cargo build --release

# Tests ausführen
test:
    cargo test

# Formatieren und linten
check:
    cargo fmt --check
    cargo clippy

# Aufräumen
clean:
    cargo clean

tree

BefehlBeschreibung
treeVerzeichnisbaum anzeigen
tree -L 2Nur 2 Ebenen tief
tree -aAuch versteckte Dateien anzeigen
tree -dNur Verzeichnisse anzeigen
tree -I "node_modules"Ordner ausschließen
tree -I "*.pyc"Dateien nach Muster ausschließen
tree -I "node_modules|.git|__pycache__"Mehrere Muster ausschließen
tree -P "*.py"Nur bestimmte Dateien anzeigen
tree -hDateigrößen anzeigen (human-readable)
tree -sDateigrößen in Bytes anzeigen
tree -DÄnderungsdatum anzeigen
tree -pBerechtigungen anzeigen
tree -uBesitzer anzeigen
tree -fVolle Pfade anzeigen
tree -o output.txtIn Datei speichern
tree --gitignore.gitignore-Regeln beachten
tree -CFarbige Ausgabe (Standard im Terminal)
tree -nKeine Farben

Terminals und Erweiterungen

Im folgenden werden neben dem Standard-Terminal weitere beliebte Terminal-Apps für macOS vorgestellt. Es wird kurz erklärt, was sie besonders machen und welche Vor- und Nachteile sie haben.

1 Begriffserklärungen

In diesem Abschnitt wird erklärt, was Terminals, Shells, Frameworks und Prompts sind und wie sie aufeinander aufbauen.

1.1 Terminal

Beschreibung:

Das Fensterprogramm, das du auf deinem Computer öffnest, um Textbefehle einzugeben. Ein Terminal ist die Benutzeroberfläche, die mit einer Shell kommuniziert.

Beispiele:

  • Apple Terminal (Standard in macOS)
  • iTerm2
  • Warp

Aufgabe:

  • Zeigt Eingaben, Ausgaben, Farben, Tabs usw.
  • Es führt selbst keine Befehle aus, sondern reicht sie an die Shell weiter.

1.2 Shell

Beschreibung:

Die Kommandozeilen-Umgebung, die Befehle interpretiert und ausführt. Sie ist das eigentliche “Gehirn” hinter dem Terminal.

Beispiele:

  • bash (ältere Standardshell)
  • zsh (Standard auf macOS seit Catalina)
  • fish (freundlich & modern)
  • nushell (strukturiert & datenorientiert)
  • PowerShell (Microsoft, aber auch für macOS/Linux verfügbar)

Aufgabe:

  • Befehle entgegennehmen, interpretieren und ausführen
  • Skripte schreiben, Variablen verwalten, Prozesse steuern

1.3 Framework

Beschreibung:

Ein Erweiterungssystem für eine Shell (meist für zsh), das Plugins, Themes und Konfigurationen organisiert. Es macht die Shell komfortabler, modularer und automatisierter.

Beispiele:

  • Oh My Zsh (riesige Plugin-Sammlung für zsh)
  • Prezto (leichtgewichtige, schnellere Alternative zu Oh My Zsh)
  • Zinit, Zplug, Antigen, Znap (Plugin-Manager für zsh)

Aufgabe:

  • Plugins laden (z. B. für Git, Docker, Syntax-Highlighting, Autocomplete)
  • Themes aktivieren
  • Die Shell beim Start konfigurieren

1.4 Prompt

Beschreibung:

Der visuelle Teil der Shell, d. h. der Zeile, die eingegeben wird. Ein Prompt zeigt z. B. Ordner, Git-Status, Uhrzeit, Exit-Code oder andere Informationen.

Beispiele:

  • Starship (universeller, moderner Prompt für alle Shells)
  • Powerlevel10k (extrem anpassbar, für zsh)
  • Pure (minimalistisch)
  • Spaceship (optisch sehr ansprechend, Git-Infos integriert)

Aufgabe:

  • Das “Gesicht” der Shell
  • Zeigt Informationen und Statusindikatoren in Echtzeit

1.5 Zusammenspiel von Terminal, Shell, Framework und Prompt

┌──────────────────────────────────────────┐
│      Terminal (z. B. iTerm2, Warp)       │
│       → öffnet und zeigt die Shell       │
│                                          │
│ ┌──────────────────────────────────────┐ │
│ │     Shell (z. B. zsh oder bash)      │ │
│ │          → für Befehle aus           │ │
│ │                                      │ │
│ │ ┌──────────────────────────────────┐ │ │
│ │ │   Framework (z. B. Oh My Zsh)    │ │ │
│ │ │  → verwaltet Plugins und Themes  │ │ │
│ │ │                                  │ │ │
│ │ │ ┌──────────────────────────────┐ │ │ │
│ │ │ │   Prompt (z. B. Starship)    │ │ │ │
│ │ │ │   → Zeigt Info und Design    │ │ │ │
│ │ │ └──────────────────────────────┘ │ │ │
│ │ └──────────────────────────────────┘ │ │
│ └──────────────────────────────────────┘ │
└──────────────────────────────────────────┘
EbeneBeispieleHauptfunktionLäuft …Wichtig für
TerminaliTerm2, WarpDarstellung, Tabs, Farben, UIunabhängigBenutzeroberfläche
Shellzsh, bashBefehlseingabe & Ausführungim TerminalLogik & Skripting
FrameworkOh My Zsh, PreztoPlugin-Management & Themesin der ShellKomfort & Automatisierung
PromptStarship, PureVisuelles Erscheinungsbild & Statusim Framework oder direkt in ShellÜbersicht & Design

note

Terminal zeigt → Shell führt aus → Framework organisiert → Prompt präsentiert

1.6 Beispiele für Kombinationen von Terminal, Shell, Framework und Prompt

NutzerprofilTerminalShellFrameworkPrompt
macOS-StandardnutzerApple TerminalzshStandard zsh Prompt
Entwickler & Power-UseriTerm2 / Warpzsh– / Oh My Zsh– / Warp Built-in Prompt / Powerlevel10k
Cross-Plattform-EntwicklerTabbybash / fishStarship
Minimalist / Performance-EnthusiastAlacrittyzshPrezto / ZnapStarship
Team & KI-orientiertWarpzshOh My ZshWarp Built-in Prompt oder Starship

2 Terminals für macOS

2.1 Apple Terminal

Webseite, kein GitHub

Beschreibung:

Das vorinstallierte Terminal von macOS. Es ist schlicht, zuverlässig und tief ins System integriert.

Vorteile:

  • Keine Installation erforderlich :luc_arrow_right: direkt einsatzbereit
  • Sehr stabil und ressourcenschonend
  • Vollständig kompatibel mit macOS (z. B. Shell-Integration, Sicherheit, Schriftarten)

Nachteile:

  • Keine modernen Komfortfunktionen (Tabs, Split Views, Themes sind rudimentär)
  • Wenig anpassbar ohne externe Tools
  • Kein “modernes” UI oder interaktive Features

2.2 iTerm2

Webseite, GitHub

Beschreibung:

Ein extrem beliebter und mächtiger Terminal-Ersatz, der seit Jahren als Standard für Power-User gilt.

Vorteile:

  • Tabs, Split Panes, Hotkeys und Profile
  • Sehr anpassbar (Themes, Transparenz, Triggers, Status Bar)
  • Mächtige Such- und Verlaufsfunktionen
  • Unterstützung für Maus-Events und sogar Inline-Bilder
  • Kostenlos und Open Source

Nachteile:

  • Die vielen Funktionen und Optionen können anfangs überfordern

2.3 Warp

Warp, GitHub

Beschreibung:

Ein modernes Terminal mit Rust-Backend und graphischer Shell-Erweiterung. Warp bietet Funktionen für Terminalarbeit mit KI, Autocomplete und Teamunterstützung.

Vorteile:

  • Sehr modernes UI mit Markdown, Blöcken und Befehlshistorie
  • KI-Autocomplete & Chat-Integration
  • Kollaborations-Features (Team-Sharing von Commands, Workflows)
  • Sehr hohe Leistung dank Rust

Nachteile:

  • Proprietär (nicht Open Source)
  • Cloud-Features erfordern Account

2.4 Alacritty

Webseite, GitHub

Beschreibung:

Minimalistisches, GPU-beschleunigtes Terminal, das auf maximale Performance und Einfachheit ausgelegt ist.

Vorteile:

  • Extrem schnell (GPU-Rendering)
  • Cross-Plattform und Open Source
  • Konfiguration über YAML-Datei – einfach für Entwickler

Nachteile:

  • Keine GUI-Einstellungen (alles per Config-Datei)
  • Keine Tabs oder Splits nativ (nur über Multiplexer wie tmux)
  • Weniger Komfortfunktionen

2.5 Kitty

Webseite, GitHub

Beschreibung:

Modernes, GPU-beschleunigtes Terminal mit Fokus auf Geschwindigkeit und Erweiterbarkeit.

Vorteile:

  • Schnell, leicht und optisch modern
  • Unterstützung für Tabs, Splits, Grafiken und Emojis
  • Erweiterbar durch Python-Skripte

Nachteile:

  • Komplexe Konfiguration
  • Weniger benutzerfreundlich als andere Terminals

2.6 Hyper

Webseite, GitHub

Beschreibung:

Auf Electron basierendes Terminal mit Webtechnologien (HTML, CSS, JS).

Vorteile:

  • Sehr anpassbar dank Plugins und Themes (JavaScript-basiert)
  • Attraktives, modernes Design
  • Ideal für Webentwickler, die JS beherrschen

Nachteile:

  • Electron-basiert → höherer RAM-Verbrauch als native Terminals
  • Teilweise instabil bei langen Sessions
  • Weniger performant

2.7 Tabby

Webseite, GitHub

Beschreibung:

Mmoderner, plattformübergreifender Terminal-Emulator mit Fokus auf Benutzerfreundlichkeit, Design und Erweiterbarkeit. Geschrieben in TypeScript/Electron.

Vorteile:

  • Sehr schönes, modernes UI
  • Tabs, Splits, SSH-Manager, Serial Support
  • Plugins und Themes (JS-basiert)
  • Cross-Plattform (macOS, Linux, Windows)

Nachteile:

  • Electron-basiert → höherer RAM-Verbrauch als native Terminals
  • Nicht so schnell wie native Terminals (z. B. iTerm2 oder Alacritty)
  • Weniger stabil bei extrem langen Sessions

2.8 Ghostty

Webseite, GitHub

Beschreibung:

Moderner Terminal-Emulator von Mitchell Hashimoto (HashiCorp-Gründer), geschrieben in Zig. Ghostty kombiniert hohe Performance durch GPU-Beschleunigung mit einer plattformnativen UI (Swift/AppKit auf macOS, GTK4 auf Linux).

Vorteile:

  • Sehr schnell durch GPU-Rendering (Metal auf macOS, OpenGL auf Linux)
  • Plattformnative UI – fühlt sich wie eine echte Mac-App an
  • Exzellente Defaults – funktioniert ohne Konfiguration out-of-the-box
  • Einfache Konfiguration über Key-Value-Paare in ~/.config/ghostty/config
  • Tabs, Splits, hunderte eingebaute Themes
  • Unterstützt Kitty Graphics Protocol und Kitty Keyboard Protocol
  • Nerd Fonts funktionieren direkt ohne Setup
  • Open Source

Nachteile:

  • Relativ neu (Version 1.0 Ende 2024)
  • Kein Windows-Support (nur macOS und Linux)
  • Weniger etabliert und dokumentiert als iTerm2

2.9 Vergleich

TerminalOpen SourceLeistungAnpass-
barkeit
Ober-
fläche
Moderne Features
(KI, Blöcke, Teams)
Schwierig-
keitsgrad
Apple Terminal🟢🔴Klassisch🔴🟢
iTerm2🟢🟢🟢🟢Klassisch🔴🟡
Warp🟢🟢🟡Modern🟢🟢🟢🟢
Alacritty🟢🟢🟢🟡Modern🔴🟡
Kitty🟢🟢🟢Modern🔴🟡
Hyper🟡🟢🟢Modern🟡🟢
Tabby🟡🟢🟢Sehr Modern🟡🟢
Ghostty🟢🟢🟢🟡Modern/Nativ🟡🟢

Zusammenfassung:

  • Apple Terminal (Webseite, kein GitHub) Standard-Terminal von macOS, stabil und einfach, aber ohne moderne Features.

  • iTerm2 (Webseite, GitHub) Sehr funktionsreich: Tabs, Splits, Triggers, Profile, Open Source.

  • Warp (Warp, GitHub) Rust-basiertes Terminal mit KI, Blöcken, Workflows und Team-Sharing.

  • Alacritty (Webseite, GitHub) GPU-beschleunigt, blitzschnell und minimalistisch, ideal mit tmux.

  • Kitty (Webseite, GitHub) Schnelles, GPU-basiertes Terminal mit Tabs, Splits und Python-Erweiterungen.

  • Hyper (Webseite, GitHub) Electron-Terminal mit Webtechnologien, sehr anpassbar durch Plugins.

  • Tabby (Webseite, GitHub) Electron-basiert, schönes Design, Plugins, Themes, SSH-/Serial-Manager.

  • Ghostty (Webseite, GitHub) Zig-basiert, GPU-beschleunigt, plattformnative UI, exzellente Defaults.

Nutzungsempfehlung nach Anwendungsfall:

AnwendungsfallEmpfohlenes Terminal
Einsteiger & MinimalistenApple Terminal
Power-User & EntwickleriTerm2
Teamarbeit & KI-UnterstützungWarp
Maxiamle Performance & TilingAlacritty oder Kitty
Web-/JS-EntwicklerHyper oder Tabby
SSH & Multi-Session-ManagementTabby
Native macOS-Experience & PerformanceGhostty

3 Shells für macOS

3.1 zsh (Z Shell)

Webseite, GitHub

Beschreibung:

Standard-Shell auf macOS seit Catalina (v. 10.15; 2019). Zsh ist mächtig, skriptfähig und stark erweiterbar.

Vorteile:

  • Modern, interaktive Features (Tab-Completion, Globbing)
  • Viele Plugins und Themes verfügbar (z. B. via Oh My Zsh)
  • Kompatibel zu bash-Skripten

Nachteile:

  • Einige ältere bash-Skripte erfordern Anpassungen
  • Steile Lernkurve bei komplexen Konfigurationen

3.2 bash

Webseite, GitHub

Beschreibung:

Ältere Standardshell, sehr verbreitet und auf fast allen Unix-Systemen verfügbar.

Vorteile:

  • Stabil und gut dokumentiert
  • Große Community & viele Tutorials
  • Skripting sehr zuverlässig

Nachteile:

  • Weniger moderne Features als zsh oder fish
  • Interaktive Nutzung weniger komfortabel

3.3 fish (Friendly Interactive Shell)

Webseite, GitHub

Beschreibung:

Benutzerfreundliche, moderne Shell, fokussiert auf einfache Nutzung und Autovervollständigung.

Vorteile:

  • Auto-Suggestions & Syntax-Highlighting standardmäßig
  • Einfach zu konfigurieren, keine Frameworks nötig
  • Moderne Features out-of-the-box

Nachteile:

  • Nicht 100 % POSIX-kompatibel → manche Skripte funktionieren nicht direkt
  • Kleinere Community als zsh/bash

3.4 nushell

Webseite, GitHub

Beschreibung:

Datenorientierte Shell, die Tabellen und strukturierte Daten direkt verarbeiten kann.

Vorteile:

  • Ideal für Workflows mit CSV, JSON, SQL-Daten
  • Modernes CLI mit strukturierter Ausgabe
  • Cross-Plattform

Nachteile:

  • Neue Syntax → Umstieg für erfahrene bash/zsh-Nutzer aufwendig
  • Weniger verbreitet, kleineres Ökosystem

3.5 PowerShell

Webseite, GitHub

Beschreibung:

Microsoft-Shell, ursprünglich für Windows, jetzt cross-platform. Fokus auf Skripterstellung und Automatisierung.

Vorteile:

  • Mächtige Skriptmöglichkeiten, Object-Pipeline
  • Plattformübergreifend (Windows, macOS, Linux)
  • Große Community & Module verfügbar

Nachteile:

  • Für macOS-Nutzer ungewohnt (anderer Syntaxstil)
  • CLI-Optik weniger minimalistisch

3.6 Vergleich

ShellOpen SourceLeistungInteraktive FeaturesSkriptfähigkeitLernkurve
bash🟢🟢🟢🟡🟢🟢🟢🟡
zsh🟢🟢🟢🟢🟢🟢🟢🟡
fish🟢🟢🟢🟢🟢🟢🟢🟡
nushell🟢🟢🟢🟢🟢🟢🟢
PowerShell🟢🟢🟢🟢🟢🟢🟢🟢

Zusammenfasung:

  • zsh (Webseite, GitHub) Erweiterte Shell mit Autovervollständigung, Themes, Plugins; beliebt für interaktives Arbeiten und Anpassung.

  • bash (Webseite, GitHub) Klassische Unix-Shell, sehr verbreitet, ideal für Scripting und Automatisierung, stabil und universell einsetzbar.

  • fish (Webseite, GitHub) Benutzerfreundlich, moderne Features wie Autosuggestions und Syntax Highlighting, weniger skriptorientiert, einfach zu lernen.

  • nushell (Webseite, GitHub) Daten-orientierte Shell für strukturiertes Arbeiten mit Tabellen, JSON, CSV; modernes CLI-Konzept.

  • PowerShell (Webseite, GitHub) Microsoft-Shell, sehr mächtig für Systemadministration, plattformübergreifend, stark auf Objekte statt Text fokussiert.

Nutzungsempfehlung nach Anwendungsfall:

AnwendungsfallEmpfohlene Shell
Skripting & Automatisierungbash / PowerShell
Interaktive Nutzung & Auto-Vervollständigungzsh / fish
Datenorientiertes Arbeiten (JSON, CSV)nushell
Windows-spezifische AdministrationPowerShell

4 Frameworks

Shell-Frameworks erleichtern die Verwaltung von Plugins, Themes und Konfigurationen für zsh (und teilweise andere Shells).

4.1 Oh My Zsh

Webseite, GitHub

Beschreibung:

Bekanntestes zsh-Framework, enthält hunderte Plugins und Themes.

Vorteile:

  • Einfach zu installieren und konfigurieren
  • Riesiges Plugin-Ökosystem
  • Große Community

Nachteile:

  • Etwas langsamer beim Starten bei vielen Plugins
  • Überladen für Minimalisten

4.2 Prezto

GitHub

Beschreibung:

Leichtgewichtige Alternative zu Oh My Zsh, schneller und schlanker.

Vorteile:

  • Schneller als Oh My Zsh
  • Modularer Aufbau
  • Viele nützliche Plugins

Nachteile:

  • Weniger Themes als bei Oh My Zsh
  • Kleinere Community als bei Oh My Zsh

4.3 Zinit

GitHub

Beschreibung:

Zinit (früher zplugin) ist ein sehr flexibler und leistungsstarker Zsh-Plugin-Manager, der gezieltes Laden von Plugins, Snippets und Themes erlaubt. Unterstützt auch Turbo-Features wie Lazy-Loading und asynchrones Laden.

Vorteile:

  • Extrem flexibel, unterstützt komplexe Konfigurationen
  • Sehr schnell durch Lazy-Loading von Plugins
  • Unterstützt Snippets, Hooks und parallele Updates

Nachteile:

  • Hohe Einarbeitungszeit
  • Komplexere Konfiguration kann unübersichtlich werden

4.4 Zplug

GitHub

Beschreibung:

Zplug ist ein moderner Zsh-Plugin-Manager, der Plugin-Installation, Updates und Lazy-Loading vereinfacht. Plugins und Themes können zentral verwaltet werden.

Vorteile:

  • Einfache Installation und Verwaltung von Plugins
  • Unterstützt Lazy-Loading für Leistung
  • Gute Community und aktive Weiterentwicklung

Nachteile:

  • Weniger mächtig als Zinit bei komplexen Setups
  • Dokumentation teilweise lückenhaft

4.5 Antigen

GitHub

Beschreibung:

Antigen ist ein minimalistischer Zsh-Plugin-Manager, der von Oh My Zsh inspiriert ist. Er lädt Plugins und Themes auf einfache Weise und eignet sich besonders für Nutzer, die schnell starten wollen.

Vorteile:

  • Schnell und einfach zu konfigurieren
  • Unterstützt Oh-My-Zsh-, Prezto- und andere Plugins
  • Leichtgewichtig

Nachteile:

  • Weniger Funktionen im Vergleich zu Zinit oder Zplug
  • Eher für einfache bis mittlere Konfigurationen geeignet

4.6 Znap

GitHub

Beschreibung:

Znap ist ein moderner, minimalistischer Plugin-Manager für Zsh, der besonders auf Performance setzt. Plugins werden nur bei Bedarf geladen, Updates sind einfach und schnell.

Vorteile:

  • Sehr schnell durch Lazy-Loading
  • Einfaches Update-Management
  • Minimalistische und saubere Konfiguration

Nachteile:

  • Weniger bekannt, kleinere Community
  • Weniger Features als Zinit für komplexe Setups

4.7 Vergleich

FrameworkOpen SourceSetup-AufwandPlugin-UnterstützungTheme-UnterstützungWartung / StabilitätLernkurve
Oh My Zsh🟢🟢🟢🟢🟢🟢🟢🟡
Prezto🟢🟢🟢🟢🟢🟢🟢🟢🟡
Zinit🟡🟢🟢🟢🟢🟢🟢🟢🟢
Zplug🟢🟢🟢🟢🟢🟢🟢🟢🟢🟢
Antigen🟡🟢🟢🟢🟢🟢🟢🟢🟢
Znap🟢🟢🟢🟢🟢🟢🟢🟢

Zusammenfassung:

  • Oh My Zsh (Webseite, GitHub) Einsteigerfreundliches Framework für zsh, viele Plugins und Themes, einfache Installation, weit verbreitet.

  • Prezto (GitHub) Schlankes, performantes zsh-Framework, schneller als Oh My Zsh, stabil, viele nützliche Defaults und Optimierungen.

  • Zinit (GitHub) Extrem anpassbar, leistungsstarker Plugin-Manager für zsh, viele Optimierungen möglich, für Power-User.

  • Zplug (GitHub) Moderne Alternative zu Antigen/Zinit, Plugin-Management und Themes für zsh, leichtgewichtig, erweiterbar.

  • Antigen (GitHub) Plugin-Manager für zsh, hohe Flexibilität, unterstützt Themes, viele Plugins, gut für fortgeschrittene Nutzer.

  • Znap (GitHub) Minimalistischer, performanter Zsh-Plugin-Manager, der Plugins nur bei Bedarf lädt, schnelle Updates, einfache Konfiguration für fortgeschrittene Nutzer.

Nutzungsempfehlung nach Anwendungsfall:

AnwendungsfallEmpfohlenes Framework
Einsteiger, schnelle InstallationOh My Zsh
Schlank & performantPrezto
Viele Plugins & ThemesAntigen / Zinit / Zplug
Power-User, maximale AnpassbarkeitZinit / Zplug

5 Prompts

Prompts sind die sichtbaren Eingabezeilen in der Shell, oft mit Statusinformationen, Farben und Symbolen.

5.1 Starship

Webseite, GitHub

Beschreibung:

Universeller Prompt für alle Shells, modern, schnell und anpassbar.

Vorteile:

  • Cross-Shell & Cross-Platform
  • Sehr anpassbar mit einfacher Konfigurationsdatei
  • Schnelle Performance

Nachteile:

  • Manche Features müssen manuell konfiguriert werden
  • Für komplette Anpassung Kenntnisse von TOML nötig

5.2 Powerlevel10k

GitHub

Beschreibung:

Extrem anpassbarer Prompt für zsh, bekannt für Performance und Optik.

Vorteile:

  • Interaktives Setup, sehr individuell
  • Git-Status, Icons, Farben, Unicode-Zeichen
  • Schnell trotz umfangreicher Features

Nachteile:

  • Nur für zsh
  • Einarbeitung nötig, wenn viele Features aktiviert werden

5.3 Pure

GitHub

Beschreibung:

Minimalistischer, sauberer Prompt für zsh und bash.

Vorteile:

  • Minimalistisch & schnell
  • Fokus auf Klarheit
  • Zeigt Git-Status, aber keine unnötigen Extras

Nachteile:

  • Weniger visuelle Features
  • Kaum Anpassungsmöglichkeiten

5.4 Spaceship

GitHub

Beschreibung:

Optisch ansprechender zsh-Prompt, zeigt Git-Info, Node-Version, Docker-Status uvm.

Vorteile:

  • Viele Funktionen direkt integriert
  • Optisch sehr ansprechendes Design
  • Open Source

Nachteile:

  • Nur für zsh
  • Kann bei umfangreichen Infos den Start verzögern

5.5 Vergleich

PromptOpen SourceGeschwindigkeitAnpassbarkeitShell-KompatibilitätFeatureumfangLernkurve
Starship🟢🟢🟢🟢🟢🟢Alle gängigen🟢🟢🟢🟡
Powerlevel10k🟢🟢🟢🟢🟢🟢zsh🟢🟢🟢🟢
Pure🟢🟢🟢🟢zsh🟢🟡
Spaceship🟢🟢🟢🟢zsh🟢🟢🟢🟡🟢

Zusammenfassung:

  • Starship (Webseite, GitHub) Cross-Shell Prompt, modern, schnell, sehr anpassbar, zeigt Git-Status, Systeminfo und weitere Features.

  • Powerlevel10k (GitHub) Hochgradig anpassbarer zsh-Prompt, sehr performant, attraktive Optik, Git-Integration, beliebt bei Power-Usern.

  • Pure (GitHub) Minimalistischer zsh-Prompt, schnell, schlicht, zeigt Git-Status, ideal für Nutzer, die wenig Schnickschnack wollen.

  • Spaceship (GitHub) Funktionsreicher zsh-Prompt, viele Module und Themes, Git-Integration, modern und optisch ansprechend.

Nutzungsempfehlung nach Anwendungsfall:

AnwendungsfallEmpfohlener Prompt
Cross-Shell, moderne FeaturesStarship
Git-Status & maximale Optik (zsh)Powerlevel10k
Minimalistisch & schnellPure
Viele Features & ansprechendes DesignSpaceship

6 Installation von Oh My Zsh und Starship

6.1 Oh My Zsh

Installation:

sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

note

Durch die Installation wird die Datei ~/.zshrc überschrieben! Zuvor wird jedoch ein Backup als ~/.zshrc.pre-oh-my-zsh erstellt. Alle benutzerdefinierten Inhalte (Aliase, Funktionen, usw.) müssen manuell in die neue Datei kopiert werden.

Anpassung:

Über Konfigurationsdatei:

~/.zshrc

Deinstallation:

Mit dem folgenden Befehl wird Oh My Zsh gelöscht und die Einstellungen vom Zeitpunkt vor der Installation (:luc_arrow_right: ursprüngliche ~/.zshrc) werden wieder hergestellt:

uninstall_oh_my_zsh

Sie auch: Uninstalling Oh My Zsh.

6.1.1 Plugins für Oh My Zsh

Vollständige Liste aller Plugins:

https://github.com/ohmyzsh/ohmyzsh/wiki/plugins

Besonders praktische Plugins:

  1. zsh-autosuggestions, zsh-syntax-highlighting, zsh-fast-syntax-highlighting und zsh-autocomplete.md Verbessert die Effizienz beim Tippen im Terminal:

    • zsh-autosuggestions schlägt automatisch vorherige Befehle vor
    • zsh-syntax-highlighting und zsh-fast-syntax-highlighting heben die Eingabe farblich hervor
    • zsh-autocomplete ergänzt Befehle und Dateinamen in Echtzeit
git clone https://github.com/zsh-users/zsh-autosuggestions.git $ZSH_CUSTOM/plugins/zsh-autosuggestions
git clone https://github.com/zsh-users/zsh-syntax-highlighting.git $ZSH_CUSTOM/plugins/zsh-syntax-highlighting
git clone https://github.com/zdharma-continuum/fast-syntax-highlighting.git ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/plugins/fast-syntax-highlighting
git clone --depth 1 -- https://github.com/marlonrichert/zsh-autocomplete.git $ZSH_CUSTOM/plugins/zsh-autocomplete
  1. sudo Schreibt sudo vor den aktuellen und vorherigen Befehl durch zweimalige Drücken der ESC-Taste.

  2. web-search Starte eine Websuche vom Terminal aus, z. B. mit dem Befehl google.

  3. copyfile Kopiert den Inhalt einer Datei in die Zwischenablage.

  4. copybuffer Mit STRG+O wird der aktuelle Text/Befehl in die Zwischenablage kopiert.

  5. dirhistory Es werden Tastaturkurzbefehle hinzugefügt um durch Ordner zu navigieren.

  6. jsontools Leserliche Darstellung von JSON-Daten.

6.2 Starship

Installation:

brew install starship

Anpassung:

Über Konfigurationsdatei:

~/.config/starship.toml

Eine Erklärung, wie sich die aktuelle Prompt zusammensetzt liefert der folgende Befehl:

starship explain

../_bilder/Terminal-Referenz für macOS - Teil 4 - Terminals, Shells & Erweiterungen-1.png

Deinstallation:

brew uninstall starship

Siehe auch: How do I uninstall Starship?

tip

Für maximale Anpassungsmöglichkeiten empiehlt sich die Verwendung einer Nerd Font, welche zusätzliche Symbole liefert.

Beliebte Programme für die Kommandozeile

ProgrammBeschreibung
batWie cat, aber mit zusätzlichen Funktionen wie Syntaxhighlighting und Git-Integration
bcKommandozeilen-Taschenrechner für mathematische Ausdrücke
claude-codeNutzung von Claude-AI-Modellen, besonders für Programmier- und Analyse-Workflows
fzfFuzzy-Finder für die Kommandozeile – interaktives Suchen in Dateien, Verzeichnissen, Git-History usw.
gituiTerminal-UI für Git – intuitive, schnelle grafische Bedienung von Git-Workflows direkt im Terminal
helixModerne modale Texteditor-Alternative zu Vim/NeoVim mit LSP-Integration und innovativem Bedienkonzept
htopInteraktiver Prozess- und Ressourcenmonitor, erweiterte Version von top
mcMidnight Commander: Dateimanager im Terminal in Anlehnung an Norton Commander (Total Commander für die Kommandozeile)
lsdWie ls, aber mit Farben, Icons, Baumansicht, besserem Format und erweiterten Optionen
microBenutzerfreundlicher Terminal-Texteditor mit Kurzbefehlen wie STRG+C, STRG+V, usw.; Alternative zu nano
neovimErweiterter, moderner Vim-Fork mit Plugin-System, LSP und besserer Integration in moderne Entwicklungs-Workflows
nnnExtrem schneller, minimalistischer Dateimanager mit Plugin-System und vielen Integrationen
pandocUniverseller Dokumenten-Konverter – wandelt praktisch jedes Format in jedes andere um
rsyncLeistungsstarkes Tool für Dateisynchronisation und Backups, unterstützt Delta-Übertragung und SSH
tldrVereinfachte, praxisorientierte Hilfeseiten mit Beispielen – schnelle Alternative zu man-Pages
treeZeigt Verzeichnisstrukturen im Baumformat an – übersichtliche Alternative zu rekursivem ls.

Weitere Links zu Midnight Commander:

tip

Weitere Programme für das Terminal:

  • Ausführliche und kategorisierte Liste weiterer Programme für die Kommandozeile: GitHub: awesome-macos-commandline
  • Sammlung von CLI-Tools: https://terminaltrove.com

Gemeinsamkeiten mit Linux-Terminals

Vieles von dem, was für macOS-Terminals gelten, ist direkt auf Linux übertragbar, es gibt jedoch einige wichtige Unterschiede. In diesem Kapitel erfolgt eine genauere Betrachtung.

1 Shell

  • macOS: Standardmäßig nutzt macOS seit macOS Catalina zsh (vorher war es bash).
  • Linux: Die häufigste Shell ist bash, aber auch zsh, fish oder andere Shells sind möglich.

Bedeutung:

  • Grundlegende Befehle (ls, cd, cp, mv, mkdir, rm, cat, grep, usw.) funktionieren auf beiden Systemen gleich.
  • Skripte in bash funktionieren oft auch in zsh, aber manche zsh-spezifischen Features gibt es auf Linux nicht.

2 Dateisystem

  • macOS: HFS+ oder APFS. Pfade beginnen mit / wie bei Linux, aber es gibt kleine Unterschiede:

    • macOS ist standardmäßig case-insensitive, d. h. file.txt = File.txt.
    • macOS hat viele macOS-spezifische Verzeichnisse wie /Applications oder /System.
  • Linux: Typisch ist ext4, XFS usw. Linux ist case-sensitive, d. h. file.txt != File.txt.

3 Paketverwaltung

  • macOS: Homebrew (brew) ist die gängigste Lösung.
  • Linux: Abhängig von Distribution:
    • Debian/Ubuntu → apt (apt-get)
    • Fedora → dnf
    • Arch → pacman

Bedeutung: Viele Tools, die man mit brew install toolname installiert, haben auf Linux andere Paketnamen oder Befehle.

4 Systembefehle / Administratorrechte

  • macOS: sudo funktioniert wie auf Linux. Systemkonfigurationen laufen oft über grafische Apps oder /etc.
  • Linux: sudo oder direkt als root (su) üblich. Systemdienste verwaltest man meistens mit systemctl.

5 Texteditoren

Sowohl macOS als auch Linux haben nano, vim und emacs. Unterschiede gibt es höchstens bei vorinstallierten Versionen.

6 Fazit

Wenn man das macOS-Terminal gut beherrscht:

  • Alltägliche Navigation, Dateimanipulation, Skripting, Textbearbeitung, Pipes und Redirects sind sehr ähnlich.
  • Systemverwaltung, Paketverwaltung, manche Pfade und systemnahe Befehle unterscheiden sich und müssen gelernt werden.

Die meisten Terminal-Befehle sind identisch, nur Systemdetails und Paketverwaltung unterscheiden sich.

KategorieBefehl / AufgabemacOS TerminalLinux TerminalHinweis
NavigationAktuelles Verzeichnispwdpwdidentisch
Verzeichnis wechselncd /Pfadcd /Pfadidentisch
Inhalt anzeigenlslsidentisch; Optionen wie ls -l gleich
Verzeichnis erstellenmkdir namemkdir nameidentisch
Dateien kopierencp quelle zielcp quelle zielidentisch
Dateien verschieben/umbenennenmv quelle zielmv quelle zielidentisch
Datei löschenrm dateirm dateiidentisch; Vorsicht bei rm -rf
Text und SucheDatei anzeigencat dateicat dateiidentisch
Zeilen durchsuchengrep "text" dateigrep "text" dateiidentisch
Texteditornano datei, vim dateinano datei, vim dateiidentisch, oft schon vorinstalliert
System & InfoBenutzerwhoamiwhoamiidentisch
Systeminfouname -auname -aidentisch
Speicherplatzdf -hdf -hidentisch
Prozesse anzeigentoptop oder htophtop evtl. separat installieren
Rechte & AdministrationAls Admin ausführensudo befehlsudo befehlidentisch
Root werdensudo -isudo -i oder su -kleine Unterschiede
Systemdienstelaunchctlsystemctlunterschiedlich, Linux nutzt meist systemctl
PaketverwaltungSoftware installierenbrew install paketsudo apt install paketPaketnamen unterscheiden sich oft
Software suchenbrew search paketapt search paketgleiches Prinzip, andere Befehle
Shell / SkripteShell startenzshbash (häufig), zshSkripte meist kompatibel, zsh-spezifische Features evtl. anders
NetzwerkIP-Adresse anzeigenifconfigip addrLinux nutzt moderne ip-Befehle häufiger
Netzwerk testenping hostping hostidentisch