Linux System Administration

Shell und Dateien: Pfad, Umfang und atomarer Tausch

Ein einziger Befehl schafft auf einem Linux-Server Fakten: Dateien sind weg, eine Konfiguration ist überschrieben, ein Dienst startet nicht mehr. Dieses Modul ist der Einstieg in den Track und beantwortet die drei Fragen, die vor jedem Eingriff stehen — was ein Befehl wirklich anfasst, wie du vor dem Löschen siehst, was genau getroffen wird, und wie du eine Datei änderst, ohne dass ein Programm sie halbfertig liest. Vorkenntnisse brauchst du keine; Begriffe wie Symlink und Mount werden unterwegs erklärt.

Lehrtext · 10 Abschnitte · zuletzt geprüft: 2026-09-03

Der Name, den du tippst, ist nicht der Ort, den du triffst

Ein Befehl mit Root-Rechten schafft Fakten. rm löscht, mv ersetzt, ein Skript schreibt eine Konfiguration, die ein Dienst gleich liest. Rückgängig macht das niemand. Bevor du so etwas ausführst, musst du drei Fragen getrennt beantworten können: Wo landet der Befehl wirklich, wie viel trifft er, und wie wird der neue Zustand übergeben.

Die drei Fragen fallen auseinander, weil ein Pfad in Linux nicht zwingend ein Ort ist. Er kann auch ein Verweis sein, dem das System erst folgen muss.

SymlinkEin symbolischer Link — ein Eintrag im Dateisystem, der nur auf einen anderen Pfad zeigt, ähnlich einer Verknüpfung. Wohin er zeigt, kann sich ändern, ohne dass sich sein Name ändert.

Ein Beispiel: /srv/app/current ist ein Symlink und zeigt heute auf /srv/app/releases/2024-11-a, nach dem nächsten Deployment auf ein anderes Verzeichnis — vielleicht sogar auf einer anderen Festplatte. Der Name in deinem Befehl bleibt gleich, die Datei dahinter nicht.

MerksatzEin stabiler Name ist keine Zusage über ein stabiles Ziel. Wer den Namen prüft statt das aufgelöste Ziel, prüft die falsche Sache.

Vier Größen, die du nicht in einen Blick ziehen darfst

Sichtbarer Pfad
Was in deiner Zeile steht. Sagt nichts über Ziel, Dateisystem oder Umfang.
Aufgelöstes Ziel
Der reale Ort nach Auflösung aller Symlinks — das, was der Kernel tatsächlich anfasst.
Umfang
Die konkrete Liste der Objekte, die der Befehl berührt. Ein Glob wie * ist keine Liste, sondern ein Versprechen.
Übergabe
Wie der neue Zustand den alten ablöst — und ob dazwischen ein halbfertiger Zustand sichtbar wird.

Auch die Frage „darf ich das überhaupt?“ gehört nicht dazu. root zu sein beantwortet keine der vier Fragen. Ein Recht bestätigt die Erlaubnis, nicht die Richtigkeit.

Was die Shell mit deinem Befehl macht, bevor er läuft

Bevor wir über Pfade reden, muss eine Sache klar sein, die viele überrascht: Wenn du rm *.log tippst, sieht rm niemals ein Sternchen. Die Shell ersetzt das Muster vorher durch die Liste der passenden Dateinamen und übergibt erst diese Liste an das Programm.

GlobbingDas Ersetzen von Mustern wie * oder ? durch die tatsächlich passenden Dateinamen — erledigt von der Shell, bevor der Befehl startet. Das Programm bekommt fertige Namen, kein Muster.

Das hat drei praktische Folgen. Erstens: Was du bekommst, hängt davon ab, was gerade im Verzeichnis liegt. Derselbe Befehl trifft morgen andere Dateien. Zweitens: Passt das Muster auf nichts, bleibt es bei vielen Shells als Text stehen — und das Programm bekommt ein Argument, das aussieht wie ein Muster. Drittens, und das ist die gefährliche Folge: Ein Dateiname kann aussehen wie eine Option.

Die Falle mit dem BindestrichLiegt in einem Verzeichnis eine Datei namens -rf, dann macht die Shell aus rm * den Aufruf rm -rf … — und rm liest das als Option, nicht als Dateiname. Genau dagegen gibt es die Trennmarke --: Alles danach behandeln die üblichen Werkzeuge als Dateinamen, egal womit es anfängt.

Verwandt damit ist das Problem mit Leerzeichen und Zeilenumbrüchen in Dateinamen. Wer eine Liste von Namen zeilenweise weiterreicht, zerlegt einen Namen mit Leerzeichen in zwei. find kann seine Ergebnisse deshalb mit einem Null-Byte statt eines Zeilenumbruchs trennen (-print0), und das Gegenstück liest sie genauso (xargs -0). Ein Null-Byte kann in einem Dateinamen nicht vorkommen — deshalb ist das die einzige eindeutige Trennung.

Und noch ein Detail, das im Alltag beißt: Ein Muster wie * erfasst standardmäßig keine Dateien, deren Name mit einem Punkt beginnt. Wer ein Verzeichnis „mit * leert“, lässt versteckte Dateien stehen — und wundert sich, dass Konfiguration überlebt hat, die eigentlich weg sollte.

Kurzcheck

Du willst in /var/tmp/export alle Dateien löschen. Warum ist rm -rf * dort riskanter als eine Liste, die du dir vorher angesehen hast?

  • Weil die Shell das Muster gegen den aktuellen Verzeichnisinhalt auflöst — du löschst, was jetzt da ist, nicht was du geprüft hast.
  • Weil rm bei einem Muster rekursiv arbeitet und ohne Muster nur die oberste Ebene löscht.
  • Weil * auch versteckte Dateien erfasst und damit mehr trifft als eine sichtbare Liste.

Treffer. Genau. Zwischen deiner Annahme und der Ausführung kann sich der Inhalt geändert haben, und ein Name, der wie eine Option aussieht, kommt noch dazu. Die angesehene Liste ist die einzige, über die du eine Aussage treffen kannst.

Das echte Ziel auflösen: readlink -f und findmnt -T

Bevor ein destruktiver Befehl läuft, lässt du dir das echte Ziel zeigen. readlink -f /srv/app/current und realpath /srv/app/current folgen der Symlink-Kette bis zum realen Objekt und geben dir den Pfad, den der Befehl trifft.

Die zweite Frage ist, welches Dateisystem diesen Pfad bedient. findmnt -T /srv/app/current beantwortet genau das: Quelle, Ziel und Typ des Mounts, unter dem der Pfad liegt. Das ist keine Formalie — es entscheidet gleich darüber, ob dein Dateitausch atomar sein kann.

Mount-GrenzeDie Linie, an der ein Pfad von einem Dateisystem in ein anderes wechselt. Sie ist im Pfad nicht sichtbar, aber für Löschen, Verschieben und Rechte entscheidend.

Neben dem Symlink gibt es eine zweite Art von Verweis, die sich völlig anders verhält — und die Verwechslung kostet regelmäßig Daten.

Symlink
Zeigt auf einen Pfad. Verschwindet das Ziel, zeigt der Link ins Leere. Kann über Dateisystemgrenzen hinweg zeigen. Löschst du den Link, bleibt das Ziel.
Hardlink
Ein zweiter Name für dieselbe Datei. Beide Namen sind gleichwertig; es gibt kein „Original“. Nur innerhalb eines Dateisystems möglich. Die Daten verschwinden erst, wenn der letzte Name weg ist.

stat zeigt dir zu einer Datei unter anderem, wie viele Namen auf sie verweisen. Steht dort mehr als einer, ist das Löschen dieses einen Namens folgenlos für die Daten — und das Bearbeiten der Datei wirkt sich auf alle anderen Namen aus. Genau deshalb brechen manche Editoren Hardlinks: Sie schreiben nicht in die Datei, sondern legen eine neue an und benennen sie über den alten Namen.

Rechte: was rwx wirklich bedeutet

Zu jeder Datei gehören ein Eigentümer, eine Gruppe und drei Rechtetripel: eines für den Eigentümer, eines für die Gruppe, eines für alle übrigen. Jedes Tripel besteht aus r (lesen), w (schreiben) und x (ausführen). Geschrieben wird das oft als dreistellige Zahl, weil sich die drei Bits als Ziffer zusammenfassen lassen: r=4, w=2, x=1.

0644
Eigentümer darf lesen und schreiben, Gruppe und alle anderen nur lesen. Der Normalfall für eine Konfigurationsdatei.
0640
Wie oben, aber Außenstehende dürfen gar nichts. Der Normalfall, sobald etwas Schützenswertes drinsteht.
0600
Nur der Eigentümer, sonst niemand. Für Schlüssel und Geheimnisse.
0755
Alle dürfen lesen und ausführen, nur der Eigentümer schreiben. Der Normalfall für Verzeichnisse und Programme.
Bei Verzeichnissen bedeuten die Buchstaben etwas anderesAuf einem Verzeichnis heißt r: die Einträge auflisten. w: Einträge anlegen, umbenennen und löschen. x: durch das Verzeichnis hindurchgehen, um an etwas darin heranzukommen. Ohne x nützt dir r nichts — du siehst die Namen, kommst aber an keine Datei.

Aus dem w am Verzeichnis folgt eine Regel, die fast alle zuerst falsch haben: Ob du eine Datei löschen oder anlegen darfst, entscheidet das Verzeichnis, nicht die Datei. Eine Datei, die dir nicht gehört und die du nicht schreiben darfst, kannst du trotzdem löschen, wenn dir das Verzeichnis gehört. Umgekehrt scheitert das Löschen einer eigenen Datei, wenn das Verzeichnis dir verschlossen ist.

Damit das in gemeinsam genutzten Verzeichnissen nicht zum Problem wird, gibt es das Sticky Bit. Ist es auf einem Verzeichnis gesetzt, darf eine Datei darin nur noch vom Eigentümer der Datei, vom Eigentümer des Verzeichnisses oder von einem privilegierten Prozess umbenannt oder gelöscht werden. Genau deshalb kann in /tmp jeder schreiben, aber niemand die Dateien der anderen wegräumen.

umaskEine Maske, die festlegt, welche Rechte einer neu erzeugten Datei von vornherein fehlen. Sie erklärt, warum eine frisch geschriebene Datei oft nicht die Rechte hat, die du erwartest — gesetzt wurden sie nie, sie wurden weggenommen.

Wenn also ein Befehl mit Permission denied abbricht, ist das eine Aussage über ein Symptom. Die Ursache steht in drei Angaben, die du dir ansiehst: unter welchem Benutzer der Befehl läuft, wem Datei und Verzeichnis gehören, und welche Rechtetripel dort stehen. stat zeigt dir das alles auf einmal.

Fehlannahme„Ich mache es kurz mit chmod 0777 auf, dann klappt es.“ Es klappt — und danach darf jeder Benutzer auf dem System die Datei ändern. Bei einer Konfiguration, die ein Dienst mit Root-Rechten liest, ist das kein Umweg, sondern eine Hintertür.

Umfang: erst sehen, dann löschen

Der zweite Schritt ist die Kandidatenliste. Der billigste Schutz gegen einen zu breiten Löschlauf ist, denselben Ausdruck zuerst anzeigen zu lassen und erst danach löschen zu lassen — dieselben Filter, nur ein anderes Endverb.

Alte Exporte aufräumen, ohne das Release zu treffen

Szenario

Unter /srv/export/current sollen Dateien älter als 30 Tage weg. Der Pfad ist ein Symlink auf das aktive Release, und darunter ist zusätzlich ein Netzlaufwerk eingehängt.

Anforderungen

  • Nur Dateien im gemeinten Verzeichnis, nicht im ganzen Baum darunter
  • Nicht über eine Mount-Grenze hinauslaufen
  • Die Liste muss vor dem Löschen sichtbar sein

Schritte

  1. Ziel auflösen: readlink -f /srv/export/current — arbeite ab hier mit dem aufgelösten Pfad, nicht mit dem Symlink.
  2. Dateisystem bestätigen: findmnt -T <aufgelöster Pfad>.
  3. Kandidaten anzeigen: find <pfad> -xdev -maxdepth 1 -type f -mtime +30 -print.
  4. Liste lesen und plausibilisieren — Anzahl und Stichproben gegen den erwarteten Datenbestand.
  5. Erst jetzt dasselbe Prädikat mit -delete statt -print ausführen.

Merksatz: -xdev hält den Lauf auf einem Dateisystem, -maxdepth begrenzt die Tiefe, -print vor -delete macht den Umfang überprüfbar.

Die drei Begrenzungen im vierten Schritt lohnen einen zweiten Blick, weil jede eine andere Art von Unfall verhindert. -xdev sorgt dafür, dass der Lauf nicht in ein eingehängtes Netzlaufwerk absteigt — dort liegen fremde Daten, und ein Löschlauf über eine langsame Netzverbindung ist zusätzlich schwer abzubrechen. -maxdepth 1 verhindert, dass Unterverzeichnisse mitgenommen werden, an die niemand gedacht hat. Und -type f stellt sicher, dass nur Dateien gemeint sind, keine Verzeichnisse.

Fehlannahme„Der Name ist stabil, also kann ich rm -rf direkt darauf loslassen.“ Der Name ist stabil, das Ziel nicht — und -rf unterdrückt genau die Rückfragen, die dich schützen würden.

Was Löschen wirklich tut — und warum der Platz manchmal nicht zurückkommt

„Löschen“ ist ein irreführendes Wort. Was tatsächlich passiert, ist das Entfernen eines Namens aus einem Verzeichnis. Die Daten dahinter verschwinden erst, wenn kein Name mehr auf sie zeigt — und wenn kein Prozess sie mehr geöffnet hat.

Der zweite Teil ist der, der im Betrieb überrascht. Solange irgendein Prozess die Datei noch offen hat, bleibt sie bestehen: Sie existiert weiter, bis der letzte Dateideskriptor auf sie geschlossen ist. Erst dann wird der belegte Platz zur Wiederverwendung freigegeben.

Das Dateisystem ist voll, obwohl du aufgeräumt hast

Szenario

Ein Dienst hat eine riesige Logdatei geschrieben. Du hast sie gelöscht, aber die Belegung des Dateisystems ist unverändert.

Anforderungen

  • Verstehen, warum der Platz nicht zurückkommt
  • Den Verursacher finden, ohne auf Verdacht Dienste neu zu starten

Schritte

  1. Der Name ist weg, die Datei nicht: Der schreibende Prozess hält sie weiterhin offen, also bleibt der Platz belegt.
  2. Nach Prozessen suchen, die gelöschte Dateien offen halten — Werkzeuge wie lsof markieren solche Einträge ausdrücklich als gelöscht.
  3. Den gefundenen Dienst kontrolliert dazu bringen, den Deskriptor zu schließen — je nach Anwendung durch ein Neuöffnen der Logdatei oder einen geplanten Neustart.
  4. Danach die Belegung erneut prüfen; jetzt erst ist der Platz zurück.

Merksatz: Wenn Löschen keinen Platz freigibt, ist die Frage nicht „habe ich genug gelöscht“, sondern „wer hält die Datei noch offen“.

Die praktische KonsequenzEine Logdatei einfach zu löschen, während der Dienst weiterschreibt, ist deshalb keine gute Idee: Der Platz bleibt belegt, und der Dienst schreibt in eine Datei, die niemand mehr sehen kann. Genau dafür gibt es Logrotation, die dem Dienst mitteilt, dass er neu öffnen soll.

Übergabe: der atomare Tausch und die Falle dabei

Schreibt ein Skript direkt in /etc/app/config.yaml, kann ein Abbruch eine halb geschriebene Datei hinterlassen — und der Dienst liest sie beim nächsten Reload. Der etablierte Ausweg ist der Tausch über einen temporären Namen: erst vollständig schreiben, dann prüfen, dann umbenennen.

atomarEine Operation, die für alle anderen Beteiligten entweder ganz oder gar nicht stattgefunden hat. Es gibt keinen beobachtbaren Zwischenzustand — niemand kann die Datei „halb ersetzt“ vorfinden.

Das Umbenennen trägt, weil es die Zieldatei atomar ersetzt: Es gibt keinen Moment, in dem ein anderer Prozess den Zielnamen fehlend oder halbfertig vorfindet. Genau diesen Aufruf benutzt mv — allerdings nur unter einer Bedingung.

Die FalleDas atomare Umbenennen funktioniert nur innerhalb eines Dateisystems. Über eine Mount-Grenze hinweg scheitert es mit dem Fehler EXDEV, und mv fällt still auf Kopieren-und-Löschen zurück. Wer die temporäre Datei in /tmp erzeugt und nach /etc schiebt, hat den atomaren Tausch verloren, ohne eine Fehlermeldung zu sehen.

Deshalb gehört die temporäre Datei in dasselbe Verzeichnis wie das Ziel, etwa über mktemp mit einer Vorlage im Zielverzeichnis. Danach setzt du Rechte und Eigentümer auf der temporären Datei — nicht auf dem Ziel, sonst existiert wieder ein Fenster mit falschen Rechten.

Die Reihenfolge ist dabei kein Geschmack, sondern folgt aus dem Ziel: Jeder Schritt, der noch schiefgehen kann, muss vor dem Sichtbarwerden passieren. Erzeugen, befüllen, validieren, Rechte setzen — und ganz zuletzt der eine unteilbare Schritt, der den neuen Zustand sichtbar macht.

Kurzcheck

Ein Skript schreibt nach /tmp/app.conf.new, validiert die Datei und führt dann mv /tmp/app.conf.new /etc/app/app.conf aus. /tmp liegt auf einem eigenen Dateisystem. Was ist das Problem?

  • Der Tausch ist nicht atomar — über die Mount-Grenze wird kopiert und gelöscht, der Dienst kann eine unvollständige Datei lesen.
  • mv kann grundsätzlich nicht nach /etc schreiben, dafür braucht es install.
  • Die Validierung kommt zu früh; sie müsste nach dem Verschieben laufen.

Treffer. Genau. Das Umbenennen scheitert über Dateisystemgrenzen mit EXDEV; mv weicht dann auf Kopieren aus. Die Lösung ist nicht ein anderes Kommando, sondern ein temporärer Name im Zielverzeichnis.

Rechte sind eine eigene Frage, keine Abkürzung

Wenn ein Schreibvorgang an Rechten scheitert, ist die Versuchung groß, die Zieldatei weit zu öffnen. Das löst weder die Atomizität noch die Validierung — es fügt nur einen Manipulationspfad hinzu, über den ein anderer Prozess die Datei zwischen Prüfung und Nutzung verändern kann.

Die saubere Variante setzt Modus, Eigentümer und Gruppe explizit auf der temporären Datei, bevor sie sichtbar wird. install kann das in einem Schritt (install -m 0640 -o root -g app …) — es ersetzt allerdings nicht den atomaren Tausch, weil es kopiert statt umzubenennen. Für den Tausch bleibt es beim Umbenennen im selben Verzeichnis.

Verlass dich dabei nicht auf die Voreinstellung. Eine mit mktemp erzeugte Datei bekommt bewusst enge Rechte — was gut ist, aber eben nicht die Rechte, die dein Dienst am Ende braucht. Und eine Datei, die dein Skript per Umleitung erzeugt, bekommt die Rechte, die die umask übrig lässt. In beiden Fällen gilt: Wenn dir der Modus wichtig ist, setze ihn.

Die Reihenfolge, die alles zusammenhält

Ziel auflösen (readlink -f) und Dateisystem bestätigen (findmnt -T)Rechte an Datei und Verzeichnis prüfen (stat)Umfang anzeigen (find … -print), bevor ein Endverb greiftNeuen Zustand vollständig im Zielverzeichnis erzeugen und validierenAtomar umbenennen und die Dienstwirkung verifizieren

Lesen vor Schreiben ist hier kein Stilprinzip, sondern die einzige Reihenfolge, in der die vier Größen einzeln überprüfbar bleiben. Jede Stufe beantwortet eine Frage, die sonst unbeantwortet in die Ausführung mitgeht — und dort mit der großzügigsten Annahme beantwortet wird.

Was du mitnehmen solltestDer Name ist nicht das Ziel. Das Muster ist nicht die Liste. Die Meldung ist nicht die Ursache. Und „gelöscht“ ist nicht „weg“. Jedes dieser vier Paare hat einen eigenen Prüfschritt — und jeder Schritt kostet Sekunden, die ein Wiederherstellungsversuch nicht kostet, sondern Stunden.

Jetzt anwenden

Diesen Stoff gibt es als Modul mit bewerteten Entscheidungs-Checks — dieselbe Einführung, danach die Übungen.

Zum Modul →
Quellen & Aktualität14 Primärquellen · zuletzt geprüft:
  1. 01man7.org/linux/man-pages/man2/rename.2.html
  2. 02man7.org/linux/man-pages/man2/unlink.2.html
  3. 03man7.org/linux/man-pages/man7/inode.7.html
  4. 04man7.org/linux/man-pages/man7/path_resolution.7.html
  5. 05man7.org/linux/man-pages/man1/realpath.1.html
  6. 06man7.org/linux/man-pages/man1/find.1.html
  7. 07man7.org/linux/man-pages/man8/findmnt.8.html
  8. 08man7.org/linux/man-pages/man1/mktemp.1.html
  9. 09man7.org/linux/man-pages/man1/install.1.html
  10. 10man7.org/linux/man-pages/man1/chmod.1.html
  11. 11man7.org/linux/man-pages/man1/stat.1.html
  12. 12gnu.org/software/bash/manual/bash.html
  13. 13gnu.org/software/coreutils/manual
  14. 14man7.org/linux/man-pages/man1/df.1.html