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.
Symlink — Ein 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.
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.
Globbing — Das 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.
-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
rmbei 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-Grenze — Die 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.
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.
umask — Eine 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.
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
- Ziel auflösen:
readlink -f /srv/export/current— arbeite ab hier mit dem aufgelösten Pfad, nicht mit dem Symlink. - Dateisystem bestätigen:
findmnt -T <aufgelöster Pfad>. - Kandidaten anzeigen:
find <pfad> -xdev -maxdepth 1 -type f -mtime +30 -print. - Liste lesen und plausibilisieren — Anzahl und Stichproben gegen den erwarteten Datenbestand.
- Erst jetzt dasselbe Prädikat mit
-deletestatt-printausfü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.
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
- Der Name ist weg, die Datei nicht: Der schreibende Prozess hält sie weiterhin offen, also bleibt der Platz belegt.
- Nach Prozessen suchen, die gelöschte Dateien offen halten — Werkzeuge wie
lsofmarkieren solche Einträge ausdrücklich als gelöscht. - Den gefundenen Dienst kontrolliert dazu bringen, den Deskriptor zu schließen — je nach Anwendung durch ein Neuöffnen der Logdatei oder einen geplanten Neustart.
- 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“.
Ü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.
atomar — Eine 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.
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.
mvkann grundsätzlich nicht nach/etcschreiben, dafür braucht esinstall.- 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
readlink -f) und Dateisystem bestätigen (findmnt -T)→Rechte an Datei und Verzeichnis prüfen (stat)→Umfang anzeigen (find … -print), bevor ein Endverb greift→Neuen Zustand vollständig im Zielverzeichnis erzeugen und validieren→Atomar umbenennen und die Dienstwirkung verifizierenLesen 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.
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:
- 01man7.org/linux/man-pages/man2/rename.2.html
- 02man7.org/linux/man-pages/man2/unlink.2.html
- 03man7.org/linux/man-pages/man7/inode.7.html
- 04man7.org/linux/man-pages/man7/path_resolution.7.html
- 05man7.org/linux/man-pages/man1/realpath.1.html
- 06man7.org/linux/man-pages/man1/find.1.html
- 07man7.org/linux/man-pages/man8/findmnt.8.html
- 08man7.org/linux/man-pages/man1/mktemp.1.html
- 09man7.org/linux/man-pages/man1/install.1.html
- 10man7.org/linux/man-pages/man1/chmod.1.html
- 11man7.org/linux/man-pages/man1/stat.1.html
- 12gnu.org/software/bash/manual/bash.html
- 13gnu.org/software/coreutils/manual
- 14man7.org/linux/man-pages/man1/df.1.html