Pipes und Redirects unter Linux – |, >, >>, 2>&1 und tee erklärt
Pipes (|) und Redirects (>, >>, 2>&1) sind die Bausteine, mit denen du einfache Befehle zu mächtigen Werkzeugen kombinierst. Sie sind das, was die Linux-Shell so produktiv macht: Statt einer riesigen Applikation baust du dir aus kleinen, spezialisierten Werkzeugen genau das zusammen, was du brauchst.
Dieser Guide erklärt die Konzepte Schritt für Schritt – von den Grundlagen bis zu xargs und Here Documents.
Datenströme: stdin, stdout, stderr
Jeder Linux-Prozess hat drei Standard-Datenströme:
┌─────────────────────────────────────────┐
│ Prozess │
│ │
│ stdin (0) ←── Eingabe (Tastatur) │
│ stdout (1) ──► Ausgabe (Terminal) │
│ stderr (2) ──► Fehler (Terminal) │
└─────────────────────────────────────────┘
| Stream | Nummer | Standard | Bedeutung |
|---|---|---|---|
| stdin | 0 | Tastatur | Eingabe |
| stdout | 1 | Terminal | normale Ausgabe |
| stderr | 2 | Terminal | Fehlermeldungen |
Diese Streams lassen sich unabhängig voneinander umleiten – das ist die Grundlage für alles Weitere.
Redirects: Ausgabe in Dateien umleiten
stdout in Datei schreiben
# > : Datei neu erstellen / überschreiben
ls -la > ausgabe.txt # Ausgabe in Datei speichern
echo "Hallo Welt" > test.txt
# >> : Datei anhängen (append)
echo "Zeile 1" > log.txt
echo "Zeile 2" >> log.txt # fügt hinzu, überschreibt nicht
date >> log.txt # Datum anhängen
# Ausgabe verwerfen (nirgendwo speichern)
ls /nicht-existent > /dev/null # /dev/null = "schwarzes Loch"
Achtung:
>überschreibt ohne Warnung! Falls du das verhindern willst:set -o noclobber # in aktueller Shell ls > vorhanden.txt # Fehler: cannot overwrite existing file ls >| vorhanden.txt # Override: doch überschreiben
stdin aus Datei lesen
# < : Datei als Eingabe verwenden
wc -l < textdatei.txt # Zeilen zählen, Eingabe aus Datei
sort < unsortiert.txt # Sortieren
mail -s "Betreff" empfaenger@example.com < nachricht.txt
Ausgabe aufteilen: Beide Streams in eine Datei
# Alle Ausgaben (stdout + stderr) in eine Datei
ls /existiert /nicht-existiert > alle.txt 2>&1
# 2>&1 bedeutet: stderr (2) auf dasselbe Ziel wie stdout (1) umleiten
# Kurzform (bash 4+)
ls /existiert /nicht-existiert &> alle.txt
Pipes: Befehle verbinden
Eine Pipe | leitet den stdout eines Befehls als stdin des nächsten weiter. Befehle laufen dabei parallel.
Befehl1 | Befehl2 | Befehl3
stdout ──► stdin stdout ──► stdin stdout ──► Terminal
Grundlegende Beispiele
# Prozesse filtern
ps aux | grep nginx
# Top 10 größte Dateien
du -sh /var/log/* | sort -rh | head -10
# Wörter zählen
cat datei.txt | wc -w
# Kürzer (ohne nutzloses cat):
wc -w < datei.txt
# Zeilen sortieren und Duplikate entfernen
cat liste.txt | sort | uniq
# Leerzeilen entfernen
grep -v "^$" datei.txt
# Log-Datei live beobachten und filtern
tail -f /var/log/nginx/error.log | grep "404"
Klassische Pipe-Kombinationen
# Die 10 am häufigsten benutzten Befehle (aus History)
history | awk '{print $2}' | sort | uniq -c | sort -rn | head -10
# Alle einzigartigen IP-Adressen in einem Nginx-Log
awk '{print $1}' /var/log/nginx/access.log | sort -u
# Speicherverbrauch nach Verzeichnis sortiert
du -sh /var/* 2>/dev/null | sort -rh
# Ports die auf Verbindungen warten
ss -tlnp | grep LISTEN
# Dateigröße aller .log-Dateien zusammen
find /var/log -name "*.log" -exec du -b {} \; | awk '{sum+=$1} END {print sum " Bytes"}'
# Anzahl laufender Prozesse nach Benutzer
ps aux | awk 'NR>1 {print $1}' | sort | uniq -c | sort -rn
Pipelines verstehen: Parallelität
# Diese Pipe-Kette läuft GLEICHZEITIG:
cat /var/log/syslog | grep "error" | wc -l
# Nicht sequentiell! cat, grep und wc laufen parallel.
# cat schreibt → Kernel-Buffer → grep liest
# grep schreibt → Kernel-Buffer → wc liest
stderr umleiten und kombinieren
stderr unterdrücken oder trennen
# Fehlermeldungen unterdrücken
ls /existiert /nicht-existent 2>/dev/null
# nur normale Ausgabe erscheint, Fehler werden verworfen
# Fehler in separate Datei
ls /a /b /c > ergebnisse.txt 2> fehler.txt
# Nur Fehler anzeigen, normale Ausgabe verwerfen
command > /dev/null
# Fehler in Pipe weiterleiten
ls /a /b 2>&1 | grep "No such"
# 2>&1 muss VOR der Pipe stehen
Die Reihenfolge bei 2>&1 ist wichtig!
# RICHTIG: stderr auf stdout umleiten, dann alles in Datei
command > datei.txt 2>&1
# FALSCH: ändert nichts (stderr geht noch ins Terminal)
command 2>&1 > datei.txt
# Erklärung: 2>&1 wird ausgewertet BEVOR > datei.txt greift
# Beim falschen Beispiel: stderr → aktuelles stdout (Terminal)
# stdout → datei.txt
Fehler-Handling in Pipes
# Problem: Pipe-Exit-Code ist der des letzten Befehls
false | true
echo $? # 0 (true), obwohl false fehlschlug!
# Lösung: pipefail
set -o pipefail
false | true
echo $? # 1 (false schlägt fehl → Pipe schlägt fehl)
# Oder: jeden Exit-Code prüfen
cmd1 | cmd2 | cmd3
echo "${PIPESTATUS[@]}" # Exit-Codes aller Pipe-Glieder: z.B. "0 1 0"
tee: Ausgabe teilen
tee liest von stdin und schreibt gleichzeitig in eine Datei und auf stdout. Wie ein T-Stück in einer Wasserleitung.
stdin → tee → stdout (Terminal oder nächste Pipe)
↓
Datei
# Ausgabe im Terminal anzeigen UND in Datei speichern
ls -la | tee verzeichnis.txt
# An Datei anhängen statt überschreiben
apt upgrade 2>&1 | tee -a upgrade.log
# In mehrere Dateien gleichzeitig schreiben
echo "Test" | tee datei1.txt datei2.txt datei3.txt
# Als root in Datei schreiben (sudo + tee statt sudo >)
echo "127.0.0.1 meinhost" | sudo tee -a /etc/hosts
# tee in der Mitte einer langen Pipeline (zum Debuggen)
cat input.txt | grep "wichtig" | tee zwischenergebnis.txt | wc -l
sudo + tee: Dateien mit Root-Rechten schreiben
# FUNKTIONIERT NICHT (sudo gilt nur für echo, nicht für Redirect):
sudo echo "text" >> /etc/hosts
# RICHTIG (tee läuft mit sudo):
echo "text" | sudo tee -a /etc/hosts
# In Datei schreiben ohne auf Terminal auszugeben:
echo "text" | sudo tee -a /etc/hosts > /dev/null
xargs: Ausgabe als Argumente nutzen
xargs nimmt stdin-Zeilen und übergibt sie als Argumente an einen Befehl. Das ist nötig, weil viele Befehle keine Pipe-Eingabe verstehen.
# Grundprinzip:
echo "file1 file2 file3" | xargs rm
# Dateien finden und löschen
find /tmp -name "*.tmp" | xargs rm -v
# Alternativ (ohne xargs):
find /tmp -name "*.tmp" -delete
# Mehrere Dateien gleichzeitig verarbeiten
ls *.txt | xargs wc -l
# Leerzeichen in Dateinamen: \0 Trenner verwenden
find . -name "*.jpg" -print0 | xargs -0 mv -t /bilder/
# Interaktiv: Bestätigung pro Datei
ls *.bak | xargs -I{} rm -i {}
# Parallelisierung: 4 Prozesse gleichzeitig
find . -name "*.png" | xargs -P4 -I{} convert {} {}.jpg
# xargs mit Platzhalter (-I{})
echo "world" | xargs -I{} echo "Hello, {}!"
# Ausgabe: Hello, world!
# Maximale Argumente pro Aufruf (-n)
echo "a b c d e" | xargs -n2 echo
# a b
# c d
# e
Here Documents und Here Strings
Here Document (<<)
Ein Here Document sendet mehrere Zeilen als stdin an einen Befehl – nützlich für Konfigurationsdateien oder mehrzeilige Eingaben.
# Grundform: << MARKER bis MARKER
cat << EOF
Zeile 1
Zeile 2
Variable wird expandiert: $HOME
EOF
# Kein Expansion mit einfachen Anführungszeichen:
cat << 'EOF'
$HOME wird hier NICHT expandiert
EOF
# Datei erstellen
cat > /etc/nginx/conf.d/meine-seite.conf << EOF
server {
listen 80;
server_name example.com;
root /var/www/html;
}
EOF
# SSH-Befehl mit mehreren Zeilen
ssh server << EOF
sudo apt update
sudo apt upgrade -y
sudo systemctl restart nginx
EOF
# Als root in Datei schreiben
sudo tee /etc/sysctl.d/99-custom.conf << EOF
net.ipv4.ip_forward = 1
vm.swappiness = 10
EOF
Here String (<<<)
Schickt eine einzelne Zeichenkette als stdin:
# Einfaches Beispiel
wc -w <<< "Hello World"
# Ausgabe: 2
# Variable verarbeiten
text="Hallo Welt"
echo $ # Länge: 10
# Alternative zu echo | Befehl:
md5sum <<< "test"
# entspricht:
echo -n "test" | md5sum
# bc für Berechnungen
echo "scale=2; 22/7" | bc
# Oder:
bc <<< "scale=4; 22/7"
Praktische Kombinationen
Log-Analyse
# Häufigste Fehler in Nginx-Log
grep "error" /var/log/nginx/error.log | \
awk '{print $NF}' | \
sort | uniq -c | \
sort -rn | head -20
# 404-Fehler der letzten Stunde zählen
awk -v d=$(date -d "1 hour ago" +%d/%b/%Y:%H) '$0 ~ d && /404/' \
/var/log/nginx/access.log | wc -l
# Live-Monitoring: Nur neue 500-Fehler anzeigen
tail -f /var/log/nginx/access.log | grep --line-buffered " 500 "
System-Aufräumen
# Größte Dateien im System finden (Top 20)
find / -type f -printf '%s %p\n' 2>/dev/null | \
sort -rn | head -20 | \
awk '{printf "%.1f MB %s\n", $1/1024/1024, $2}'
# Alte Logdateien komprimieren und verschieben
find /var/log -name "*.log" -mtime +30 | \
xargs -I{} gzip {}
# Docker aufräumen (dangling images, stopped containers)
docker ps -aq --filter status=exited | xargs -r docker rm
docker images -q --filter dangling=true | xargs -r docker rmi
Konfigurationen generieren
# Nginx-Config für mehrere Domains generieren
for domain in example.com test.de meinprojekt.io; do
cat >> /etc/nginx/conf.d/domains.conf << EOF
server {
listen 80;
server_name $domain;
return 301 https://\$host\$request_uri;
}
EOF
done
# CSV verarbeiten
cat nutzer.csv | \
grep -v "^#" | \ # Kommentarzeilen entfernen
cut -d',' -f1,3 | \ # Spalten 1 und 3 auswählen
sort -t',' -k2 | \ # Nach Spalte 2 sortieren
head -10 # Erste 10 Zeilen
Häufige Fehler
Ausgabe nicht gepuffert (Buffering-Problem)
# Problem: grep in Pipe zeigt nichts, weil Ausgabe gepuffert wird
tail -f access.log | grep "error" # Funktioniert, aber langsam
# Lösung: --line-buffered
tail -f access.log | grep --line-buffered "error"
# Für andere Befehle: stdbuf
tail -f access.log | stdbuf -oL awk '/error/{print}'
xargs und Sonderzeichen
# Problem: Dateinamen mit Leerzeichen
find . -name "*.txt" | xargs rm # FEHLER bei Leerzeichen in Namen!
# Lösung: -print0 und -0
find . -name "*.txt" -print0 | xargs -0 rm
sudo mit Pipe
# Problem: Nur der erste Befehl läuft als root
sudo cat /etc/shadow | grep root # cat als root, grep nicht nötig
# Besser: sudo für den relevanten Teil
sudo grep root /etc/shadow
# Oder: sudo bash -c für komplette Pipe
sudo bash -c 'cmd1 | cmd2'
> löscht Dateiinhalt sofort
# Problem: sort < datei.txt > datei.txt LÖSCHT DIE DATEI!
# Die Shell öffnet datei.txt für Schreiben (und leert sie) BEVOR sort liest
# Lösung: temporäre Datei oder sponge
sort datei.txt -o datei.txt # sort hat -o Option dafür
# oder:
sort datei.txt | sponge datei.txt # sponge aus moreutils
Fazit
Pipes und Redirects sind das Rückgrat der Unix-Philosophie: kleine Werkzeuge, die gut zusammenarbeiten. Die wichtigsten Punkte:
>überschreibt,>>fügt hinzu – bei wichtigen Dateien immer zweimal nachdenken2>&1leitet stderr auf stdout um – Reihenfolge beachten!|verbindet Befehle und lässt sie parallel laufenteeist die saubere Lösung für „Ausgabe anzeigen UND speichern"xargsüberbrückt Befehle, die keine Pipe-Eingabe verstehen- Bei Leerzeichen in Dateinamen: immer
find -print0 | xargs -0