Pipes und Redirects unter Linux – |, >, >>, 2>&1 und tee erklärt

Pipes und Redirects in der Linux-Shell verstehen: stdout, stderr, stdin umleiten, Befehle mit | verketten, tee für parallele Ausgabe, xargs und Here Documents – mit praktischen Beispielen.

9 min Lesezeit

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 nachdenken
  • 2>&1 leitet stderr auf stdout um – Reihenfolge beachten!
  • | verbindet Befehle und lässt sie parallel laufen
  • tee ist 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

War dieser Artikel hilfreich?