Python Datei- und Verzeichnispfad-Manipulation

PythonBeginner
Jetzt üben

Einführung

In diesem Lab lernen Sie, wie man Datei- und Verzeichnispfade in Python mithilfe des modernen pathlib-Moduls behandelt. Obwohl das ältere os.path-Modul immer noch verbreitet ist, bietet pathlib einen objektorientierten Ansatz, der die Pfadmanipulation intuitiver und lesbarer macht. Sie werden lernen, Pfade, Dateien und Verzeichnisse so zu erstellen, zu inspizieren, zu validieren und zu manipulieren, dass dies auf verschiedenen Betriebssystemen wie Windows, macOS und Linux funktioniert.

Am Ende dieses Labs werden Sie versiert sein in:

  • Korrektes Zusammenfügen von Pfadkomponenten.
  • Erstellen und Verwalten von Verzeichnissen.
  • Unterscheiden zwischen absoluten und relativen Pfaden.
  • Validieren der Existenz und des Typs von Pfaden.
  • Durchführen gängiger Dateioperationen wie Kopieren, Verschieben und Löschen.
  • Durchlaufen von Verzeichnisstrukturen, um Dateien und Ordner zu finden.

Grundlegende Pfadoperationen

In diesem Schritt lernen Sie die Grundlagen der Pfadmanipulation mit dem pathlib-Modul kennen. Dies umfasst das Zusammenfügen von Pfadkomponenten, das Abrufen des aktuellen Arbeitsverzeichnisses und das Erstellen neuer Verzeichnisse.

Das pathlib-Modul bietet eine elegante, plattformübergreifende Methode zur Handhabung von Dateisystempfaden. Unter Windows verwenden Pfade Backslashes (\), während sie auf Unix-ähnlichen Systemen (Linux, macOS) Forward Slashes (/) verwenden. Die Verwendung von pathlib stellt sicher, dass Ihr Code auf jeder Plattform ohne manuelle Anpassungen funktioniert.

Hinweis: Wenn Sie mit der Verwendung von WebIDE (ähnlich VS Code) nicht vertraut sind, lernen Sie dies bitte zuerst in diesem Lab.

Zuerst sehen wir uns an, wie man Pfadkomponenten zusammenfügt. Der /-Operator ist eine intuitive Abkürzung für joinpath().

  1. Öffnen Sie die Datei main.py über den Dateiexplorer auf der linken Seite.
  2. Ersetzen Sie den Inhalt von main.py durch den folgenden Code, um Pfadkomponenten zusammenzufügen und das Ergebnis auszugeben.
from pathlib import Path

## Join path components using the / operator
my_path = Path('usr') / 'bin' / 'spam'
print(my_path)
  1. Führen Sie das Skript im Terminal aus:
python3 main.py

Sie sehen die folgende Ausgabe, die zeigt, wie pathlib korrekt einen Unix-ähnlichen Pfad bildet:

usr/bin/spam

Als Nächstes rufen wir das aktuelle Arbeitsverzeichnis mit Path.cwd() ab.

  1. Ersetzen Sie den Inhalt von main.py durch den folgenden Code (dies ersetzt den vorherigen Code):
from pathlib import Path

## Get the current working directory
cwd = Path.cwd()
print(cwd)
  1. Führen Sie das Skript erneut aus:
python3 main.py

Die Ausgabe zeigt das aktuelle Arbeitsverzeichnis an, das in dieser Umgebung /home/labex/project ist:

/home/labex/project

Zuletzt erstellen wir ein neues Verzeichnis. Ein FileNotFoundError tritt auf, wenn Sie versuchen, ein Verzeichnis zu erstellen, dessen übergeordnetes Verzeichnis nicht existiert. Um dies zu vermeiden, verwenden Sie mkdir(parents=True), wodurch alle notwendigen übergeordneten Verzeichnisse erstellt werden.

  1. Ersetzen Sie den Inhalt von main.py durch den folgenden Code, um eine verschachtelte Verzeichnisstruktur zu erstellen:
from pathlib import Path

## Get the current working directory
cwd = Path.cwd()

## Define the path for the new nested directories
new_dirs = cwd / 'delicious' / 'walnut' / 'waffles'

## Create the directories, including any missing parents
new_dirs.mkdir(parents=True, exist_ok=True)

print(f"Successfully created: {new_dirs}")

Das Argument exist_ok=True verhindert einen Fehler, falls das Verzeichnis bereits existiert. Das bedeutet, Sie können das Skript mehrmals fehlerfrei ausführen.

  1. Führen Sie das Skript aus:
python3 main.py

Das Skript erstellt die verschachtelten Verzeichnisse und gibt eine Bestätigungsnachricht aus:

Successfully created: /home/labex/project/delicious/walnut/waffles

Sie können die Verzeichniserstellung überprüfen, indem Sie im Terminal ls -R delicious ausführen.

Absolute vs. Relative Pfade

In diesem Schritt lernen Sie, absolute und relative Pfade zu unterscheiden und damit zu arbeiten.

  • Ein absoluter Pfad beginnt am Stammverzeichnis (Root) des Dateisystems (z. B. / unter Linux oder C:\ unter Windows).
  • Ein relativer Pfad wird vom aktuellen Arbeitsverzeichnis aus definiert.

pathlib bietet Methoden, um zu prüfen, ob ein Pfad absolut ist, einen Pfad in seine absolute Form aufzulösen und einen relativen Pfad von einem Ausgangspunkt aus zu finden.

Zuerst prüfen wir mit is_absolute(), ob ein Pfad absolut ist.

  1. Ersetzen Sie den Inhalt von main.py durch den folgenden Code:
from pathlib import Path

## Check if paths are absolute
print(f"Path('/') is absolute: {Path('/').is_absolute()}")
print(f"Path('..') is absolute: {Path('..').is_absolute()}")
  1. Führen Sie das Skript aus:
python3 main.py

Die Ausgabe bestätigt, dass / ein absoluter Pfad ist, während .. (übergeordnetes Verzeichnis) relativ ist.

Path('/') is absolute: True
Path('..') is absolute: False

Als Nächstes können Sie den vollständigen absoluten Pfad eines relativen Pfades mithilfe von resolve() abrufen. Dies ist nützlich, um relative Pfade wie . (aktuelles Verzeichnis) oder .. (übergeordnetes Verzeichnis) in ihre vollständige, eindeutige Form umzuwandeln.

  1. Ersetzen Sie den Inhalt von main.py durch den folgenden Code:
from pathlib import Path

## Resolve a relative path to its absolute form
## '..' refers to the parent directory of the current working directory
resolved_path = Path('..').resolve()

print(f"Current working directory: {Path.cwd()}")
print(f"Resolved path of '..': {resolved_path}")
  1. Führen Sie das Skript aus:
python3 main.py

Die Ausgabe zeigt das aktuelle Verzeichnis und den aufgelösten absoluten Pfad seines Elternverzeichnisses an.

Current working directory: /home/labex/project
Resolved path of '..': /home/labex

Zuletzt können Sie einen relativen Pfad von einem Pfad zu einem anderen mithilfe von relative_to() berechnen.

  1. Ersetzen Sie den Inhalt von main.py durch den folgenden Code, um einen relativen Pfad zu berechnen:
from pathlib import Path

## Get a relative path from a starting point
## This shows the path to 'passwd' relative to the root directory '/'
relative_path = Path('/etc/passwd').relative_to('/')
print(f"Path to '/etc/passwd' relative to '/': {relative_path}")
  1. Führen Sie das Skript aus:
python3 main.py

Die Ausgabe zeigt den Pfad relativ zum Stammverzeichnis:

Path to '/etc/passwd' relative to '/': etc/passwd

Pfadvalidierung und Inspektion

In diesem Schritt lernen Sie, die Eigenschaften eines Pfades zu überprüfen, z. B. ob er existiert und ob es sich um eine Datei oder ein Verzeichnis handelt. Sie erfahren auch, wie Sie Datei-Metadaten wie deren Größe abrufen können.

Eine häufige Aufgabe ist die Überprüfung, ob eine Datei oder ein Verzeichnis existiert, bevor versucht wird, darauf zuzugreifen. Die Methode exists() ist dafür perfekt geeignet.

  1. Öffnen Sie main.py und ersetzen Sie dessen Inhalt durch den folgenden Code. Dieser prüft die Existenz des aktuellen Verzeichnisses (.), einer Datei (setup.py) und einer nicht existierenden Datei.
from pathlib import Path

## Check if paths exist
print(f"Path('.') exists: {Path('.').exists()}")
print(f"Path('setup.py') exists: {Path('setup.py').exists()}")
print(f"Path('nonexistentfile') exists: {Path('nonexistentfile').exists()}")
  1. Führen Sie das Skript aus:
python3 main.py

Die Ausgabe bestätigt, welche Pfade existieren:

Path('.') exists: True
Path('setup.py') exists: True
Path('nonexistentfile') exists: False

Sie können auch spezifischer prüfen, ob ein Pfad auf eine Datei mit is_file() oder auf ein Verzeichnis mit is_dir() zeigt.

  1. Ersetzen Sie den Inhalt von main.py durch den folgenden Code, um diese Prüfungen durchzuführen:
from pathlib import Path

## Check if a path is a file
print(f"Path('setup.py').is_file(): {Path('setup.py').is_file()}")
print(f"Path('/home').is_file(): {Path('/home').is_file()}")

## Check if a path is a directory
print(f"Path('/').is_dir(): {Path('/').is_dir()}")
print(f"Path('setup.py').is_dir(): {Path('setup.py').is_dir()}")
  1. Führen Sie das Skript aus:
python3 main.py

Die Ausgabe identifiziert korrekt den Typ jedes Pfades:

Path('setup.py').is_file(): True
Path('/home').is_file(): False
Path('/').is_dir(): True
Path('setup.py').is_dir(): False

Schließlich können Sie mit der Methode stat() detaillierte Dateiinformationen, wie z. B. deren Größe, abrufen. Sie gibt ein Objekt mit verschiedenen Attributen zurück, einschließlich st_size für die Dateigröße in Bytes.

  1. Ersetzen Sie den Inhalt von main.py durch den folgenden Code, um die Größe einer Systemdatei abzurufen:
from pathlib import Path
import os

## Define the path to a file
file_path = Path('/bin/python3')

## Get file statistics
stat_info = file_path.stat()

## Print the size in bytes
print(f"File: {file_path}")
print(f"Size: {stat_info.st_size} bytes")
  1. Führen Sie das Skript aus:
python3 main.py

Die Ausgabe zeigt die Größe der Python-Ausführungsdatei in Bytes an. Die genaue Größe kann variieren.

File: /bin/python3
Size: 10024 bytes

Datei- und Ordnerverwaltung

In diesem Schritt lernen Sie, gängige Dateisystemoperationen wie das Kopieren, Verschieben, Umbenennen und Löschen von Dateien und Ordnern mithilfe des Moduls shutil in Kombination mit pathlib durchzuführen.

Das Modul shutil bietet Funktionen auf hoher Ebene für Dateioperationen. Beginnen wir mit dem Kopieren von Dateien. shutil.copy() kopiert eine einzelne Datei an einen neuen Speicherort.

  1. Öffnen Sie die Datei manage_files.py im Dateiexplorer auf der linken Seite.
  2. Ersetzen Sie den Inhalt von manage_files.py durch den folgenden Code. Dies kopiert spam.txt in das Verzeichnis /tmp/delicious.
import shutil
from pathlib import Path

## Quelle und Ziel definieren
source_file = Path('/tmp/spam.txt')
destination_dir = Path('/tmp/delicious')

## Die Datei kopieren
shutil.copy(source_file, destination_dir)

print(f"'{source_file}' nach '{destination_dir}' kopiert")

## Sie können die Datei auch beim Kopieren umbenennen
shutil.copy('/tmp/eggs.txt', '/tmp/delicious/eggs2.txt')
print("Datei 'eggs.txt' kopiert und in 'eggs2.txt' umbenannt")
  1. Führen Sie das Skript aus:
python3 manage_files.py

Um einen gesamten Verzeichnisbaum zu kopieren, verwenden Sie shutil.copytree().

  1. Fügen Sie die folgenden Zeilen am Ende von manage_files.py hinzu (fügen Sie diese Zeilen nach dem vorhandenen Code an):
## Einen gesamten Verzeichnisbaum kopieren
shutil.copytree('/tmp/bacon', '/tmp/bacon_backup')
print("Verzeichnis '/tmp/bacon' nach '/tmp/bacon_backup' kopiert")

Hinweis: Wenn Sie dieses Skript mehrmals ausführen, schlägt shutil.copytree() fehl, da das Zielverzeichnis bereits existiert. Dies ist das erwartete Verhalten. Führen Sie es vorerst nur einmal aus.

  1. Führen Sie das Skript erneut aus, um es in Aktion zu sehen.

Als Nächstes verschieben und benennen wir Dateien mit shutil.move() um. Diese Funktion kann entweder eine Datei in ein anderes Verzeichnis verschieben oder sie umbenennen.

  1. Ersetzen Sie den gesamten Inhalt von manage_files.py durch den folgenden Code, um bacon.txt zu verschieben:
import shutil
from pathlib import Path

## Das Zielverzeichnis erstellen, falls es nicht existiert
Path('/tmp/eggs').mkdir(parents=True, exist_ok=True)

## Eine Datei in ein Verzeichnis verschieben (nur wenn die Quelle existiert)
bacon_source = Path('/tmp/bacon.txt')
if bacon_source.exists():
    shutil.move('/tmp/bacon.txt', '/tmp/eggs')
    print("'/tmp/bacon.txt' nach '/tmp/eggs' verschoben")
else:
    print("'/tmp/bacon.txt' nicht gefunden (möglicherweise bereits verschoben)")

## Eine Datei verschieben und umbenennen
## Zuerst erstellen wir die Datei neu, um sie zu verschieben
Path('/tmp/bacon.txt').touch()
shutil.move('/tmp/bacon.txt', '/tmp/eggs/new_bacon.txt')
print("'/tmp/bacon.txt' nach '/tmp/eggs/new_bacon.txt' verschoben und umbenannt")

Hinweis: Der Code prüft, ob die Quelldatei existiert, bevor sie verschoben wird. Daher können Sie dieses Skript mehrmals sicher ausführen. Der zweite Teil erstellt immer eine neue Datei und verschiebt sie.

  1. Führen Sie das Skript aus.

Schließlich werfen wir einen Blick auf das Löschen von Dateien und Ordnern.

  • Path.unlink() löscht eine einzelne Datei.
  • Path.rmdir() löscht ein leeres Verzeichnis.
  • shutil.rmtree() löscht ein Verzeichnis und dessen gesamten Inhalt.
  1. Ersetzen Sie den gesamten Inhalt von manage_files.py durch Folgendes:
from pathlib import Path
import shutil

## Eine zu löschende Datei erstellen
file_to_delete = Path('/tmp/deleteme.txt')
file_to_delete.touch()
print(f"'{file_to_delete}' erstellt")

## Die Datei löschen
file_to_delete.unlink()
print(f"'{file_to_delete}' gelöscht")

## Ein zu löschendes Verzeichnis erstellen
dir_to_delete = Path('/tmp/temp_dir')
dir_to_delete.mkdir(exist_ok=True)
## rmdir() funktioniert nur bei leeren Verzeichnissen
dir_to_delete.rmdir()
print(f"Leeres Verzeichnis '{dir_to_delete}' gelöscht")

## shutil.rmtree() verwenden, um ein Verzeichnis und seinen Inhalt zu löschen
## Hinweis: Dies schlägt fehl, wenn das Verzeichnis nicht existiert, was für dieses Beispiel in Ordnung ist
if Path('/tmp/bacon_backup').exists():
    shutil.rmtree('/tmp/bacon_backup')
    print("Verzeichnis '/tmp/bacon_backup' und dessen gesamten Inhalt gelöscht")
else:
    print("Verzeichnis '/tmp/bacon_backup' existiert nicht (möglicherweise bereits gelöscht)")

Hinweis: Wenn Sie dieses Skript mehrmals ausführen, können einige Operationen fehlschlagen, da Dateien oder Verzeichnisse bereits gelöscht wurden. Dies ist normal und zeigt, dass diese Operationen nicht idempotent sind (sie können nicht sicher wiederholt werden).

  1. Führen Sie das Skript aus, um die Löschvorgänge durchzuführen.

Verzeichnisdurchlauf (Directory Traversal)

In diesem Schritt lernen Sie, wie Sie den Inhalt eines Verzeichnisses auflisten und einen Verzeichnisbaum rekursiv durchlaufen können.

Um den Inhalt eines Verzeichnisses aufzulisten, können Sie die Methode iterdir() verwenden, die einen Iterator aller Elemente (Dateien und Verzeichnisse) im Pfad zurückgibt.

  1. Zuerst richten wir eine Beispielverzeichnisstruktur ein. Führen Sie diesen Befehl im Terminal aus:
mkdir -p ~/project/data/level1 && touch ~/project/data/file0.txt ~/project/data/level1/file1.txt
  1. Öffnen Sie nun main.py und ersetzen Sie dessen Inhalt durch den folgenden Code, um den Inhalt des Verzeichnisses /usr/bin aufzulisten.
from pathlib import Path

## Inhalt eines Verzeichnisses auflisten
print("Inhalt von '/usr/bin':")
count = 0
for f in Path('/usr/bin').iterdir():
    if count < 5: ## Die ersten 5 Elemente ausgeben, um die Ausgabe kurz zu halten
        print(f)
        count += 1
    else:
        break
  1. Führen Sie das Skript aus:
python3 main.py

Die Ausgabe zeigt die ersten paar Dateien und Verzeichnisse innerhalb von /usr/bin. Die genaue Liste kann variieren.

Contents of '/usr/bin':
/usr/bin/[
/usr/bin/addpart
/usr/bin/apt
/usr/bin/apt-cache
/usr/bin/apt-cdrom

Sie können iterdir() mit stat() kombinieren, um die Gesamtgröße aller Dateien in einem Verzeichnis zu berechnen.

  1. Ersetzen Sie den Inhalt von main.py durch den folgenden Code. Beachten Sie, dass dieser Code nur die Größen der direkten Kinder summiert und nicht rekursiv in Unterverzeichnisse absteigt.
from pathlib import Path

total_size = 0
directory = Path('/usr/bin')

for sub_path in directory.iterdir():
    if sub_path.is_file(): ## Sicherstellen, dass nur die Größe von Dateien addiert wird
        total_size += sub_path.stat().st_size

print(f"Gesamtgröße der Dateien in '{directory}': {total_size} Bytes")
  1. Führen Sie das Skript aus. Es gibt die Gesamtgröße aller Dateien direkt in /usr/bin aus.

Für komplexere Aufgaben müssen Sie möglicherweise einen gesamten Verzeichnisbaum "durchlaufen" (walk), wobei alle Unterverzeichnisse und Dateien besucht werden. Die Methode rglob() ist hierfür perfekt geeignet. Sie findet rekursiv alle Pfade, die einem bestimmten Muster entsprechen. Die Verwendung von * als Muster passt auf alles.

  1. Erstellen Sie zunächst eine komplexere Verzeichnisstruktur für dieses Beispiel. Führen Sie dies im Terminal aus:
mkdir -p /tmp/delicious/cats /tmp/delicious/walnut/waffles
touch /tmp/delicious/spam.txt /tmp/delicious/cats/catnames.txt /tmp/delicious/walnut/waffles/butter.txt

Hinweis: Sie können diese Befehle mehrmals sicher ausführen. Der Befehl mkdir -p schlägt nicht fehl, wenn Verzeichnisse bereits existieren, und touch aktualisiert die Zeitstempel der Dateien, falls diese bereits existieren.

  1. Ersetzen Sie den Inhalt von main.py durch den folgenden Code, um das Verzeichnis /tmp/delicious zu durchlaufen:
from pathlib import Path

p = Path('/tmp/delicious')
print(f"Durchlaufe Verzeichnisbaum für: {p}")

for i in p.rglob('*'):
    print(i)
  1. Führen Sie das Skript aus:
python3 main.py

Die Ausgabe listet jede Datei und jeden Ordner innerhalb von /tmp/delicious und seinen Unterverzeichnissen auf und demonstriert so, wie rglob() den gesamten Baum durchläuft.

Walking directory tree for: /tmp/delicious
/tmp/delicious/spam.txt
/tmp/delicious/eggs2.txt
/tmp/delicious/cats
/tmp/delicious/walnut
/tmp/delicious/cats/catnames.txt
/tmp/delicious/walnut/waffles
/tmp/delicious/walnut/waffles/butter.txt

Zusammenfassung

Herzlichen Glückwunsch zum Abschluss des Labs! Sie haben gelernt, wie man Dateipfade und Verzeichnispfade in Python mithilfe des modernen Moduls pathlib effektiv verwaltet.

In diesem Lab haben Sie Folgendes geübt:

  • Erstellen und Verknüpfen von Pfaden auf plattformübergreifende Weise mithilfe von Path-Objekten und dem /-Operator.
  • Abrufen des aktuellen Arbeitsverzeichnisses und Erstellen verschachtelter Verzeichnisse mit mkdir(parents=True).
  • Unterscheiden zwischen absoluten und relativen Pfaden und Konvertieren zwischen ihnen mithilfe von is_absolute(), resolve() und relative_to().
  • Validieren von Pfaden mit exists(), is_file() und is_dir() sowie Untersuchen von Datei-Metadaten mit stat().
  • Durchführen wesentlicher Dateiverwaltungsaufgaben wie Kopieren, Verschieben und Löschen mit dem Modul shutil und pathlib-Methoden.
  • Durchlaufen von Verzeichnisstrukturen zum Auflisten von Inhalten mit iterdir() und zum rekursiven Finden von Elementen mit rglob().

Diese Fähigkeiten sind grundlegend für das Schreiben robuster und wartbarer Python-Anwendungen, die mit dem Dateisystem interagieren.