Dateisystem¶
Um mit Dateien zu arbeiten, müsst ihr häufig auch mit dem Dateisystem und den unterschiedlichen Konventionen je nach Betriebssystem interagieren. Hierfür zeige ich euch os und speziell os.path.
Pfade und Pfadnamen¶
Alle Betriebssysteme verweisen auf Dateien mit Zeichenketten, die als Pfadnamen bezeichnet werden. Python bietet eine Reihe von Funktionen, die euch helfen, einige Probleme zu lösen. Die Semantik von Pfadnamen ist auf allen Betriebssystemen sehr ähnlich, da das Dateisystem meist als Baumstruktur modelliert ist, wobei eine Festplatte die Wurzel und Ordner, Unterordner usw. die Zweige und Unterzweige darstellen; d.h., dass die meisten Betriebssysteme auf eine bestimmte Datei in sehr ähnlicher Art verweisen.
Verschiedene Betriebssysteme haben jedoch unterschiedliche Konventionen für
Pfadnamen. Das Zeichen, das zur Trennung von aufeinanderfolgenden Datei- oder
Verzeichnisnamen in einem Linux/macOS-Pfadnamen verwendet wird, ist /
,
während es in einem Windows-Pfadnamen \
ist. Außerdem hat das
Linux-Dateisystem ein einziges Stammverzeichnis auf das durch ein /
-Zeichen
als erstes Zeichen im Pfadnamen verwiesen wird, während das Windows-Dateisystem
für jedes Laufwerk ein eigenes Stammverzeichnis hat, das mit C:\
usw. bezeichnet wird. Aufgrund dieser Unterschiede haben die
Dateien auf den verschiedenen Betriebssystemen unterschiedliche Pfadnamen. Eine
Datei namens C:datamyfile
unter Windows könnte unter Linux und macOS
/data/myfile
sein. Python bietet Funktionen und Konstanten, mit denen
ihr gängige Pfadnamenmanipulationen durchführen könnt, ohne sich um solche
syntaktischen Details kümmern zu müssen. Mit ein wenig Sorgfalt können ihr eure
Python-Programme so schreiben, dass sie unabhängig vom zugrunde liegenden
Dateisystem korrekt ausgeführt werden.
Absolute und relative Pfade¶
Diese Betriebssysteme erlauben zwei Arten von Pfadnamen:
- Absolute Pfadnamen
geben die genaue Position einer Datei im Dateisystem eindeutig an, indem sie den gesamten Pfad zu dieser Datei auflisten, beginnend mit dem Wurzelverzeichnis des Dateisystems.
Als Beispiele seien hier zwei absolute Windows-Pfadnamen genannt:
C:\Program Files\Python 3.9\ D:\backup\2022\06\
Und hier sind zwei absolute Linux-Pfadnamen und ein absoluter macOS-Pfadname:
/bin/python3 /cdrom/backup/2022/06/ /Applications/Python\ 3.10/
- Relative Pfadnamen
geben die Position einer Datei relativ zu einem anderen Punkt im Dateisystem an, und dieser andere Punkt wird nicht im relativen Pfadnamen selbst angegeben.
Als Beispiel sei hier ein relativer Windows-Pfadnamen genannt:
save-data\filesystem.rst
… und hier ein relativer Linux/macOS-Pfadname:
save-data/filesystem.rst
Relative Pfade benötigen also einen Kontext, in dem sie verankert sind. Dieser Kontext wird in der Regel auf eine der beiden folgenden Arten bereitgestellt:
Der relative Pfad wird an einen vorhandenen absoluten Pfad anzuhängt, wodurch ein neuer absoluter Pfad entsteht. Wenn ihr einen relativen Windows-Pfad
Start Menu\Programs\Python 3.8
und einen absoluten PfadC:\Users\Veit
habt, dann kann durch Anhängen des relativen Pfads ein neuer absoluter Pfad:C:\Users\Veit\Start Menu\Programs\Python 3.8
erstellt werden. Wenn ihr denselben relativen Pfad an einen anderen absoluten Pfad anhängt (z.B. anC:\Users\Tim
, so erhaltet ihr einen neuen Pfad, der sich auf ein anderesHOME
-Verzeichnis (Tim
) bezieht.Relative Pfade können auch einen Kontext erhalten durch den impliziten Verweis auf das aktuelle Arbeitsverzeichnis, also das Verzeichnis, in dem sich ein Python-Programm zum Zeitpunkt seiner Ausführung, befindet. Python-Befehle können implizit auf das aktuelle Arbeitsverzeichnis zurückgreifen, wenn ihnen ein relativer Pfad als Argument übergeben wird. Wenn ihr z.B. den Befehl
os.listdir('RELATIVE/PATH')
mit einem relativen Pfadargument verwendet, ist der Anker für diesen relativen Pfad das aktuelle Arbeitsverzeichnis, und das Ergebnis des Befehls ist eine Liste der Dateinamen in dem Verzeichnis, dessen Pfad durch Anhängen des aktuellen Arbeitsverzeichnisses an das relative Pfadargument gebildet wird.Das Verzeichnis, in dem sich eine Python-Datei befindet, wird als current working directory (engl.: aktuelles Arbeitsverzeichnis) bezeichnet. Dieses Verzeichnis wird sich meist von dem Verzeichnis unterscheiden, in dem sich der Python-Interpreter befindet. Um dies zu verdeutlichen, starten wir Python und verwenden den Befehl
os.getcwd()
, um das aktuelle Arbeitsverzeichnis von Python zu ermitteln:>>> import os >>> os.getcwd() '/home/veit'
Bemerkung
os.getcwd()
wird als Funktionsaufruf ohne Argumente verwendet um zu verdeutlichen, dass der zurückgegebene Wert keine Konstante ist, sondern sich ändert, wenn ihr den Wert des aktuellen Arbeitsverzeichnisses ändert. Im obigen Beispiel ist das Ergebnis das Home-Verzeichnis auf einem meiner Linux-Rechner. Auf Windows-Rechnern würden zusätzliche Backslashes in den Pfad eingefügt:C:\\Users\\Veit
, da Windows den Backslash\
als Pfadseparator verwendet, der in Zeichenketten jedoch eine andere Bedeutung hat.Um euch die Inhalte des aktuellen Verzeichnisses anzeigen zu lassen, könnt ihr folgendes eingeben:
>>> os.listdir(os.curdir) ['.gnupg', '.bashrc', '.local', '.bash_history', '.ssh', '.bash_logout', '.profile', '.idlerc', '.viminfo', '.config', 'Downloads', 'Documents', '.python_history']
Ihr könnt jedoch auch in ein anderes Verzeichnis wechseln und euch dann das aktuelle Arbeitsverzeichnis ausgeben lassen:
>>> os.chdir('Downloads') >>> os.getcwd() '/home/veit/Downloads'
Pfadnamen ändern¶
Python bietet einige Möglichkeiten zum Ändern der Pfadnamen mit dem Submodul os.path, ohne explizit eine betriebssystemspezifische Syntax verwenden zu müssen.
os.path.join()
konstruiert Pfadnamen für verschiedene Betriebssysteme, z.B. unter Windows:
>>> import os >>> print(os.path.join('save-data', 'filesystem.rst')) save-data\filesystem.rst
Dabei werden die Argumente interpretiert als eine Reihe von Verzeichnis- oder Dateinamen, die zu einer einzigen Zeichenkette verbunden werden sollen, die vom zugrunde liegenden Betriebssystem als relativer Pfad verstanden wird. Unter Windows bedeutet dies, dass die Namen der Pfadkomponenten mit Backslashes (
\
) verbunden werden.Wenn ihr das Gleiche unter Linux/macOS ausführt, erhaltet ihr hingegen als Separator
/
:>>> import os >>> print(os.path.join('save-data', 'filesystem.rst')) save-data/filesystem.rst
Ihr könnt mit dieser Methode also Dateipfade unabhängig vom Betriebssystem, auf dem euer Programm läuft, erstellen.
Die Argumente müssen auch nicht unbedingt einzelne Verzeichnis- oder Dateinamen sein; sie können auch Unterpfade sein, die dann zu einem längeren Pfadnamen zusammengefügt werden. Das folgende Beispiel veranschaulicht dies unter Windows, wobei entweder Schrägstriche (
/
) oder doppelte Backslashes (\\
) in den Zeichenketten verwendet werden können:>>> import os >>> print(os.path.join('python-basics-tutorial-de\\docs', 'save-data\\filesystem.rst')) python-basics-tutorial-de\docs\save-data\filesystem.rst
os.path.split()
gibt ein Tupel mit zwei Elementen zurück, das den Basisnamen eines Pfades vom Rest des Pfades trennt, z.B. unter macOS:
>>> import os >>> print(os.path.split(os.getcwd())) ('/home/veit/python-basics-tutorial-de', 'docs')
os.path.basename()
gibt nur den Basisnamen des Pfades zurück:
>>> import os >>> print(os.path.basename(os.getcwd())) docs
os.path.dirname()
gibt den Pfad bis zum Basisnamen zurück:
>>> import os >>> print(os.path.dirname(os.getcwd())) /home/veit/python-basics-tutorial-de
os.path.splitext()
gibt die gepunktete Erweiterungsnotation aus, die von den meisten Dateisystemen verwendet wird, um den Dateityp anzugeben:
>>> import os >>> print(os.path.splitext('filesystem.rst')) ('filesystem', '.rst')
Das letzte Element des zurückgegebenen Tupels enthält die gepunktete Erweiterung der angegebenen Datei.
os.path.commonpath()
ist eine spezialisiertere Funktionen, um Pfadnamen zu manipulieren. Sie findet den gemeinsamen Pfad für eine Gruppe von Pfaden und ist so gut geeignet um das Verzeichnis der untersten Ebene zu finden, das jede Datei in einer Gruppe von Dateien enthält:
>>> import os >>> print(os.path.commonpath(['save-data/filesystem.rst', 'save-data/index.rst'])) save-data
os.path.expandvars()
erweitert Umgebungsvariablen in Pfaden:
>>> os.path.expandvars('$HOME/python-basics-tutorial-de') '/home/veit/python-basics-tutorial-de'
Nützliche Konstanten und Funktionen¶
os.name
gibt den Namen des Python-Moduls zurück, das importiert wurde, um die betriebssystemspezifischen Details zu handhaben, z.B.:
>>> import os >>> os.name 'nt'
Bemerkung
Die meisten Versionen von Windows, mit Ausnahme von Windows CE, werden als
nt
identifiziert.Auf macOS und Linux lautet die Antwort
posix
. Je nach Plattform könnt ihr mit dieser Antwort spezielle Operationen durchführen:>>> import os >>> if os.name == 'posix': ... root_dir = '/' ... elif os.name == 'nt': ... root_dir = 'C:\\' ... else: ... print('The operating system was not recognised!')
Informationen über Dateien erhalten¶
Dateipfade zeigen Dateien und Verzeichnisse auf eurer Festplatte an. Um mehr über sie zu erfahren, gibt es verschiedene Python-Funktionen, u.a.
os.path.exists()
gibt
True
zurück, wenn sein Argument ein Pfad ist, der mit einem im Dateisystem existierenden Pfad übereinstimmt.os.path.isfile()
gibt
True
zurück, wenn und nur wenn der angegebene Pfad auf eine Datei hinweist, und gibt andernfallsFalse
zurück, einschließlich der Möglichkeit, dass das Pfadargument auf nichts im Dateisystem hinweist.os.path.isdir()
gibt
True
zurück, wenn und nur wenn sein Pfadargument auf ein Verzeichnis hinweist; andernfalls gibt esFalse
zurück.
Weitere ähnliche Funktionen stellen speziellere Abfragen bereit:
os.path.islink()
gibt
True
zurück, wenn ein Pfad eine Datei angibt, die ein Link ist. Windows-Verknüfungsdateien mit der Endung.lnk
sind jedoch in diesem Sinne keine echten Links und gebenFalse
zurück. Nur mitmklink()
erstellte Links geben ebenfallsTrue
zurück.os.path.ismount()
gibt unter
possix
-DateisystemenTrue
zurück, wenn der Pfad ein sog. Mount Point oder Einhängepunkt ist.os.path.samefile()
gibt
True
zurück, wenn die beiden Pfadargumente auf dieselbe Datei zeigen.os.path.isabs()
gibt
True
zurück, wenn sein Argument ein absoluter Pfad ist; andernfalls wirdFalse
zurückgegeben.os.path.getsize()
gibt die Größe der Datei oder des Verzeichnisses an.
os.path.getmtime()
gibt das Änderungsdatum der Datei oder des Verzeichnisses an.
os.path.getatime()
gibt de letzte Zugriffszeit für eine Datei oder ein Verzeichnis an.
Weitere Dateisystemoperationen¶
Python verfügt über weitere, sehr nützlicher Befehle im os
-Modul:
Im Folgenden beschreibe ich nur einige betriebssystemübergreifende Operationen,
es werden jedoch auch spezifischere Dateisystemfunktionen bereitgestellt.
os.rename()
benennt oder verschiebt eine Datei oder ein Verzeichnis, z.B.
>>> os.rename('filesystem.rst', 'save-data/filesystem.rst')
os.remove()
löscht Dateien, z.B.
>>> os.remove('filesystem.rst')
os.rmdir()
löscht ein leeres Verzeichnis. Um nicht leere Verzeichnisse zu entfernen, verwendet
shutil.rmtree()
; diese Funktion entfernt rekursiv alle Dateien in einem Verzeichnisbaum.os.makedirs()
erstellt ein Verzeichnis mit allen notwendigen Zwischenverzeichnissen, z.B.
>>> os.makedirs('save-data/filesystem')
Verarbeitung aller Dateien in einem Verzeichnis¶
Eine nützliche Funktion zum rekursiven Durchlaufen von Verzeichnisstrukturen ist
die Funktion os.walk()
. Mit ihr könnt ihr einen ganzen Verzeichnisbaum
durchlaufen und für jedes Verzeichnis den Pfad dieses Verzeichnisses, eine Liste
seiner Unterverzeichnisse und eine Liste seiner Dateien zurückgeben. Dabei kann
sie drei optionale Argumente haben: os.walk(directory, topdown=True,
onerror=None, followlinks= False)
.
directory
ist der Pfad des Startverzeichnisses
topdown
auf
True
oder nicht vorhanden, verarbeitet die Dateien in jedem Verzeichnis vor den Unterverzeichnissen, was zu einer Auflistung führt, die oben beginnt und nach unten geht;auf
False
werden die Unterverzeichnisse jedes Verzeichnisses zuerst verarbeitet, was eine Durchquerung des Baums von unten nach oben ergibt.onerror
kann auf eine Funktion gesetzt werden, um Fehler zu behandeln, die aus Aufrufen von
os.listdir()
resultieren, die standardmäßig ignoriert werden. Üblicherweise wird symbolische Links nicht gefolgt, es sei denn, ihr gebt den Parameterfollow-links=True
an.
1>>> import os
2>>> for root, dirs, files in os.walk(os.curdir):
3... print("{0} has {1} files".format(root, len(files)))
4... if ".ipynb_checkpoints" in dirs:
5... dirs.remove(".ipynb_checkpoints")
6...
7. has 13 files
8./control-flows has 13 files
9./save-data has 30 files
10./test has 15 files
11./test/coverage has 3 files
12…
- Zeile 4
prüft auf ein Verzeichnis namens
.ipynb_checkpoints
.- Zeile 5
entfernt
.ipynb_checkpoints
aus der Verzeichnisliste.
shutil.copytree()
erstellt rekursiv Kopien aller Dateien eines
Verzeichnisses und all seiner Unterverzeichnisse, wobei die Informationen über
den Zugriffsmodus und den Status (d.h. die
Zugriffs- und Änderungszeiten) erhalten bleiben. shutil
verfügt auch über
die bereits erwähnte Funktion shutil.rmtree()
zum Entfernen eines
Verzeichnisses und aller seiner Unterverzeichnisse sowie über mehrere Funktionen
zum Erstellen von Kopien einzelner Dateien.