Python Grundkurs Teil 009: Projektordner mit Datum und Protokolldatei sichern
Worum geht es in diesem Teil?
In diesem Teil des Python-Grundkurses sichern wir vollständige Projektordner in neue, eindeutig datierte Backupordner. Bei jedem echten Sicherungslauf entsteht zusätzlich eine eigene Protokolldatei. Darin wird festgehalten, welche Ordner angelegt, welche Dateien kopiert und welche Elemente ausgeschlossen wurden.
Aus einem Projektordner wie:
C:\Projekte\MeinProjekt
entsteht beispielsweise diese Struktur:
C:\Projekte\MeinProjekt
└── Backups
└── Backup_2026-09-12_18-30-45
├── Dokumente
├── Bilder
└── backup_protokoll_2026-09-12_18-30-45.log

Ordner Backups mit Backup
Der vorhandene Ordner Backups wird beim Einlesen der Quelle ausdrücklich ausgeschlossen. Dadurch gelangen ältere Sicherungen nicht erneut in das nächste Backup.
Wir erstellen zwei Varianten:
- Die normale Variante sichert den Ordner, in dem das Python-Skript liegt.
- Die SendTo-Variante übernimmt einen oder mehrere Projektordner aus dem Windows-Explorer.
Was ist das Ziel?
Mit wenigen Schritten soll eine neue Momentaufnahme eines Projektordners entstehen. Datum und Uhrzeit im Ordnernamen machen jeden Sicherungslauf eindeutig und zeitlich nachvollziehbar.
Vor dem Kopieren zeigt das Skript eine vollständige Vorschau. Im Testmodus werden keine Ordner, Dateien oder Protokolle angelegt. Erst im Echtmodus und nach der genauen Eingabe JA beginnt der Kopiervorgang.
Warum ist dieser Teil wichtig?
Dieses Tutorial verbindet mehrere wichtige Python-Grundlagen mit einer praktischen Aufgabe:
datetime.now().strftime()erzeugt den Zeitstempel.Patherstellt und verarbeitet Dateisystempfade.os.walk()durchläuft einen Projektordner einschließlich seiner Unterordner.fnmatchcase()prüft Dateinamen gegen Ausschlussmuster.casefold()ermöglicht Vergleiche ohne Beachtung der Groß- und Kleinschreibung.- Listen und Dictionaries speichern den vorbereiteten Backupplan.
mkdir()legt die Zielstruktur an.shutil.copy2()kopiert Dateien und übernimmt dabei möglichst auch ihre Metadaten.withsorgt dafür, dass die Protokolldatei zuverlässig geschlossen wird.sys.argvübernimmt Testmodus und SendTo-Pfade.
Hinweis zur Kursreihe
Dieses Tutorial gehört zum Python-Grundkurs. Der Aufbau orientiert sich an den entsprechenden Aufgaben im Batch- und PowerShell-7-Grundkurs. So lassen sich die unterschiedlichen Schreibweisen und Arbeitsweisen der drei Sprachen direkt miteinander vergleichen.
Die Python-Version verwendet ausschließlich Module aus der Standardbibliothek. Zusätzliche Pakete müssen daher nicht installiert werden.
Downloads zu diesem Teil
Download Python Grundkurs: 009_Backup_mit_Datum als ZIP-Datei.Entpacken nach:
C:\TOPTORIALS_CODE\020_Python_Grundkurs
Der Hauptordner für diesen Teil lautet:
C:\TOPTORIALS_CODE\020_Python_Grundkurs\009_Backup_mit_Datum
Testumgebung zum Mitmachen
Verwende für die ersten Durchläufe ausschließlich Kopien unwichtiger Dateien. Ein kleiner Test-Projektordner mit einigen Dateien, leeren Unterordnern sowie den Ordnern Temp und Cache eignet sich besonders gut. So kannst du Vorschau, Ausschlüsse, Ordnerstruktur und Protokoll gefahrlos prüfen.
Testumgebung für Windows-Automatisierung:Download TOPTORIALS_Testdaten_Windows_Automatisierung_V001.zip
Entpacken nach:
C:\TOPTORIALS_CODE\1000_TESTBEREICH
Sicherheitshinweis
Wichtig:Prüfe vor dem Start den tatsächlichen Namen der Starter-Batchdatei. Nur wenn darin _test vorkommt, wird --test an Python übergeben.
Im Fenster muss Testmodus: JA erscheinen. In diesem Modus werden die geplanten Ordner und Dateien nur angezeigt. Es entstehen weder ein Backups-Ordner noch kopierte Dateien oder eine Protokolldatei.
Ohne _test beginnt nach der Vorschau der Echtmodus. Die Kopien werden erst nach der exakten Eingabe JA erstellt.
Ein Backup im selben Projektordner und auf demselben Datenträger schützt nicht vor einem Defekt oder Verlust dieses Datenträgers. Wichtige Daten sollten zusätzlich auf einem anderen Datenträger oder an einem anderen sicheren Ort gesichert werden.
Die Nutzung des Codes erfolgt auf eigene Verantwortung. Es wird keine Haftung für Datenverluste, unbeabsichtigte Änderungen oder andere Schäden übernommen.
Voraussetzungen
- Windows 10 oder Windows 11.
- Python 3, erreichbar über den Python Launcher
py. - Ausreichende Lese- und Schreibrechte in den Projektordnern.
- Python-Skript und passende Starter-Batchdatei liegen jeweils gemeinsam in ihrem Kursordner.
- Für SendTo wird eine Verknüpfung zur Starter-Batchdatei im Windows-Ordner Senden an angelegt.
- Externe Python-Pakete werden nicht benötigt.
Enthaltene Ordner und Dateien
Die beiden Varianten liegen in diesen Kursordnern:
C:\TOPTORIALS_CODE\020_Python_Grundkurs\009_Backup_mit_Datum\02_Projektordner_sichern_mit_Log
C:\TOPTORIALS_CODE\020_Python_Grundkurs\009_Backup_mit_Datum\03_SendTo_Projektordner_sichern_mit_Log
| Ordner | Python-Skript | Starter im Testmodus |
|---|---|---|
02_Projektordner_sichern_mit_Log |
009_backup_mit_datum_mit_log.py |
009_backup_mit_datum_mit_log_starter_batch_test.bat |
03_SendTo_Projektordner_sichern_mit_Log |
009_sendto_backup_mit_datum_mit_log.py |
009_sendto_backup_mit_datum_mit_log_starter_batch_test.bat |
Für den Echtmodus wird nur _test aus dem Namen des jeweiligen Starters entfernt. Der Name der Python-Datei bleibt unverändert, weil die Batchdatei genau diesen Namen aufruft.
Wo werden die Backups gespeichert?
Normale Variante
Die normale Variante verwendet den Ordner des Python-Skripts als Quelle:
quelle = Path(__file__).resolve().parent
__file__ enthält den Pfad der ausgeführten Python-Datei. resolve() bildet daraus einen absoluten, aufgelösten Pfad und parent liefert den übergeordneten Ordner. Der Ordner Backups entsteht deshalb direkt innerhalb dieses Projektordners.
02_Projektordner_sichern_mit_Log
├── 009_backup_mit_datum_mit_log.py
├── 009_backup_mit_datum_mit_log_starter_batch.bat
└── Backups
└── Backup_2026-09-12_18-30-45
SendTo-Variante
Bei SendTo ist nicht der Skriptordner die Quelle. Der Windows-Explorer übergibt die ausgewählten Projektordner als Argumente. In jedem gültigen Quellordner entsteht ein eigener Ordner Backups.
Ausgewählter Projektordner
└── Backups
└── Backup_2026-09-12_18-30-45
Wichtig für SendTo:Python-Skript und Starter-Batchdatei bleiben gemeinsam im Kursordner. In Senden an gehört nur eine Verknüpfung zur Starter-Batchdatei. Eine Kopie der Batchdatei würde das daneben erwartete Python-Skript dort nicht finden.
Zeitstempel, Backupordner und Protokolldatei
Python erzeugt den Zeitstempel einmal pro Skriptlauf:
zeitstempel = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
| Platzhalter | Bedeutung | Beispiel |
|---|---|---|
%Y |
Jahr mit vier Ziffern | 2026 |
%m |
Monat mit zwei Ziffern | 09 |
%d |
Tag mit zwei Ziffern | 12 |
%H |
Stunde im 24-Stunden-Format | 18 |
%M |
Minute | 30 |
%S |
Sekunde | 45 |
Daraus entstehen zum Beispiel:
Backup_2026-09-12_18-30-45
backup_protokoll_2026-09-12_18-30-45.log
Alle in einem SendTo-Durchlauf ausgewählten Quellen erhalten denselben Zeitstempel. Da jedes Projekt seinen eigenen Backups-Ordner besitzt, entstehen dadurch keine Konflikte zwischen den Quellen.
Welche Ordner und Dateien werden ausgeschlossen?
Diese Ordnernamen sind vorbereitet:
Temp
Cache
Alte Dateien
Zusätzlich wird immer der jeweilige Hauptordner Backups ausgeschlossen. Sonst könnten bereits vorhandene Sicherungen beim nächsten Lauf erneut kopiert werden.
Diese Dateien und Dateimuster werden ausgelassen:
desktop.ini
thumbs.db
*.tmp
*.log
*.bak
Die Einträge stehen am Anfang beider Python-Skripte in AUSGESCHLOSSENE_ORDNER und AUSGESCHLOSSENE_DATEIEN. Sie können dort angepasst oder erweitert werden.
casefold() sorgt bei diesen Prüfungen dafür, dass Groß- und Kleinschreibung keine Rolle spielen. Deshalb passen beispielsweise BERICHT.LOG und bericht.log beide auf *.log.
Wie entsteht der Backupplan?
Die Funktion backup_planen() durchläuft die Quelle mit:
os.walk(
quelle,
topdown=True,
onerror=walk_fehler_ausloesen,
followlinks=False,
)
| Argument | Aufgabe |
|---|---|
quelle |
Startordner für den Durchlauf. |
topdown=True |
Bearbeitet einen Ordner, bevor dessen Unterordner betreten werden. |
onerror=walk_fehler_ausloesen |
Gibt Lesefehler an den aufrufenden Code weiter. |
followlinks=False |
Folgt symbolischen Verzeichnislinks nicht. |
Besonders wichtig ist diese Zuweisung:
unterordner[:] = erlaubte_unterordner
os.walk() stellt in unterordner die als Nächstes zu besuchenden Ordner bereit. Durch die Slice-Zuweisung wird genau diese vorhandene Liste verändert. Entfernte Ordner werden anschließend nicht mehr betreten. So können Temp, Cache, Alte Dateien, Backups sowie Verzeichnisverknüpfungen vollständig vom Durchlauf ausgeschlossen werden.
Das Ergebnis ist ein Dictionary mit vier Listen:
{
"ordner": ordner,
"dateien": dateien,
"ausgeschlossene_ordner": ausgeschlossene_ordner,
"ausgeschlossene_dateien": ausgeschlossene_dateien,
}
Damit kann das Skript zuerst alles anzeigen und erst danach entscheiden, ob der Plan nur getestet oder wirklich ausgeführt wird.
Symbolische Links und Windows-Junctions
Die Funktion ist_verzeichnisverknuepfung() prüft mit is_symlink() symbolische Links. Falls die verwendete Python-Version is_junction() anbietet, werden unter Windows zusätzlich Junctions erkannt.
Solche Verknüpfungen werden nicht verfolgt. Dadurch kann der Backupdurchlauf nicht unbeabsichtigt in andere Verzeichnisbereiche wechseln oder durch eine Verknüpfung immer wieder dieselben Ordner erreichen.
Testmodus und Echtmodus
Der Starter erkennt den Modus an seinem eigenen Dateinamen:
- Enthält der Name
_test, wird Python mit--testgestartet. - Fehlt
_test, startet das Python-Skript im Echtmodus.
Python wertet den Schalter mit sys.argv aus:
testmodus = "--test" in sys.argv[1:]
Im Testmodus wird der komplette Plan angezeigt, anschließend endet das Programm mit sys.exit(0). Das Dateisystem bleibt unverändert.
Im Echtmodus erscheint die Sicherheitsabfrage:
Backup jetzt erstellen? Bitte JA eingeben:
Die SendTo-Version fragt entsprechend nach der gemeinsamen Freigabe für alle angezeigten Backups.
Optional auch ein kleines ja akzeptieren
Die getesteten Skripte verwenden:
if antwort.strip() != "JA":
strip() entfernt Leerzeichen vor und hinter der Eingabe. Der normale Zeichenkettenvergleich unterscheidet in Python Groß- und Kleinschreibung. Deshalb wird ausschließlich JA akzeptiert.
Sollen auch ja, Ja und jA gelten, kann die Zeile in beiden Python-Skripten so geändert werden:
if antwort.strip().casefold() != "ja":
Der Unterschied:casefold() erzeugt für den Vergleich eine normalisierte Kleinschreibweise. Der ursprüngliche Eingabetext wird dabei nicht verändert.
Wie werden Ordner und Dateien kopiert?
Der Zielordner wird erst nach der Bestätigung angelegt:
zielordner.mkdir(parents=True)
parents=True erlaubt Python, bei Bedarf auch den übergeordneten Ordner Backups zu erstellen. Ein bereits vorhandener Zielordner wird nicht wiederverwendet; das Skript meldet in diesem Fall einen Fehler.
Die vorbereiteten Unterordner entstehen mit mkdir(). Anschließend wird jede Quelldatei kopiert:
shutil.copy2(quelldatei, zieldatei)
copy2() kopiert den Dateiinhalt und versucht zusätzlich, Metadaten wie Änderungs- und Zugriffszeit zu übernehmen. Betriebssystemabhängig können jedoch nicht sämtliche Metadaten erhalten bleiben.
Auch leere Unterordner werden angelegt, weil Ordner und Dateien getrennt im Plan gespeichert sind.
Wie entsteht die Protokolldatei?
Die Logdatei wird im neuen Backupordner als UTF-8-Datei geöffnet:
protokoll = logdatei.open("w", encoding="utf-8")
Der Modus "w" steht für Schreiben. Da der Zielordner für jeden Durchlauf neu ist, entsteht dort auch eine neue Protokolldatei.
Mit:
with protokoll:
wird ein Kontextmanager verwendet. Beim Verlassen dieses Blocks schließt Python die Datei automatisch, auch wenn innerhalb des Blocks ein Fehler auftreten sollte.
Die Hilfsfunktion zeile_ausgeben_und_protokollieren() gibt jede Meldung im Konsolenfenster aus und schreibt dieselbe Zeile in das Protokoll. flush() übergibt die gepufferten Daten unmittelbar an das Betriebssystem.
Am Ende enthält das Protokoll unter anderem:
- Zeitstempel, Quelle und Ziel,
- die eingestellten Ausschlüsse,
- angelegte Ordner und kopierte Dateien,
- aufgetretene Fehler,
- eine Zusammenfassung mit Zählerständen.
Variante 1: Projektordner mit Logdatei sichern
So wird die Variante angewendet
Lege das Python-Skript und die Starter-Batchdatei aus 02_Projektordner_sichern_mit_Log gemeinsam in den Projektordner, der gesichert werden soll.
Starte zuerst:
009_backup_mit_datum_mit_log_starter_batch_test.bat
Prüfe die Anzeige Testmodus: JA, den Zielpfad, alle aufgelisteten Dateien und die Zähler. Im Testmodus darf noch kein Ordner Backups entstehen.
Für den Echtlauf entfernst du _test aus dem Namen der Starter-Batchdatei. Starte sie erneut und bestätige erst nach Kontrolle der neuen Vorschau mit JA.
Vollständiges Python-Skript
Dateiname: 009_backup_mit_datum_mit_log.py
# ============================================================
# TOPTORIALS OFFICE - Python Grundkurs
# Teil: 009 - Backup mit Datum und Protokolldatei
# Script: 009_backup_mit_datum_mit_log.py
# Beschreibung:
# Sichert den Ordner dieses Python-Skripts in einem datierten
# Unterordner und erstellt beim Echtlauf eine Protokolldatei.
# Autor: Chris
# YouTube: https://www.youtube.com/@TOPTORIALS-OFFICE
# Webseite: https://www.toptorials.com
# Version: Grundkurs 1.0
# Stand: 2026-09-12
#
# Hinweise:
# - Benötigt Python 3.
# - Mit --test wird nur angezeigt, was gesichert würde.
# - Im Testmodus werden keine Ordner oder Dateien angelegt.
# - Ohne --test erfolgt die Sicherung nach Bestätigung mit JA.
# - Der Ordner "Backups" wird nie in ein Backup aufgenommen.
# - Festgelegte Ordner, Dateien und Dateimuster werden ausgeschlossen.
# - Symbolische Links und Windows-Junctions werden nicht verfolgt.
# - Zuerst mit Testdaten arbeiten und wichtige Daten zusätzlich sichern.
# - Die Nutzung erfolgt auf eigene Verantwortung.
# ============================================================
from datetime import datetime
from fnmatch import fnmatchcase
import os
from pathlib import Path
import shutil
import sys
# ------------------------------------------------------------
# Einstellungen
# ------------------------------------------------------------
BACKUP_HAUPTORDNER = "Backups"
AUSGESCHLOSSENE_ORDNER = (
"Temp",
"Cache",
"Alte Dateien",
)
AUSGESCHLOSSENE_DATEIEN = (
"desktop.ini",
"thumbs.db",
"*.tmp",
"*.log",
"*.bak",
)
# ------------------------------------------------------------
# Hilfsfunktionen
# ------------------------------------------------------------
def pfad_schluessel(pfad):
"""Erzeugt einen Vergleichswert für einen vollständigen Pfad."""
return os.path.normcase(os.path.abspath(str(pfad))).casefold()
def ist_verzeichnisverknuepfung(pfad):
"""Erkennt symbolische Links und, falls verfügbar, Junctions."""
try:
if pfad.is_symlink():
return True
if hasattr(pfad, "is_junction") and pfad.is_junction():
return True
except OSError:
return True
return False
def ist_datei_ausgeschlossen(dateiname):
"""Prüft einen Dateinamen gegen alle ausgeschlossenen Muster."""
name = dateiname.casefold()
return any(
fnmatchcase(name, muster.casefold())
for muster in AUSGESCHLOSSENE_DATEIEN
)
def walk_fehler_ausloesen(fehler):
"""Gibt einen Lesefehler von os.walk() an den Aufrufer weiter."""
raise fehler
def backup_planen(quelle, backup_pfad):
"""Ermittelt alle Ordner und Dateien, die gesichert werden sollen."""
ordner = []
dateien = []
ausgeschlossene_ordner = []
ausgeschlossene_dateien = []
ausgeschlossene_ordnernamen = {
name.casefold() for name in AUSGESCHLOSSENE_ORDNER
}
backup_schluessel = pfad_schluessel(backup_pfad)
for aktueller_text, unterordner, dateinamen in os.walk(
quelle,
topdown=True,
onerror=walk_fehler_ausloesen,
followlinks=False,
):
aktueller_ordner = Path(aktueller_text)
relativer_ordner = aktueller_ordner.relative_to(quelle)
if relativer_ordner != Path("."):
ordner.append(relativer_ordner)
erlaubte_unterordner = []
for ordnername in sorted(unterordner, key=str.casefold):
ordnerpfad = aktueller_ordner / ordnername
ausgeschlossen = (
ordnername.casefold() in ausgeschlossene_ordnernamen
or pfad_schluessel(ordnerpfad) == backup_schluessel
or ist_verzeichnisverknuepfung(ordnerpfad)
)
if ausgeschlossen:
ausgeschlossene_ordner.append(ordnerpfad)
else:
erlaubte_unterordner.append(ordnername)
unterordner[:] = erlaubte_unterordner
for dateiname in sorted(dateinamen, key=str.casefold):
dateipfad = aktueller_ordner / dateiname
if ist_datei_ausgeschlossen(dateiname) or dateipfad.is_symlink():
ausgeschlossene_dateien.append(dateipfad)
else:
dateien.append(dateipfad)
return {
"ordner": ordner,
"dateien": dateien,
"ausgeschlossene_ordner": ausgeschlossene_ordner,
"ausgeschlossene_dateien": ausgeschlossene_dateien,
}
def zeile_ausgeben_und_protokollieren(protokoll, text=""):
"""Gibt eine Zeile aus und schreibt sie zusätzlich ins Protokoll."""
print(text)
protokoll.write(text + "\n")
protokoll.flush()
# ------------------------------------------------------------
# Testmodus und Pfade vorbereiten
# ------------------------------------------------------------
testmodus = "--test" in sys.argv[1:]
quelle = Path(__file__).resolve().parent
zeitstempel = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
backup_pfad = quelle / BACKUP_HAUPTORDNER
zielordner = backup_pfad / f"Backup_{zeitstempel}"
logdatei = zielordner / f"backup_protokoll_{zeitstempel}.log"
# ------------------------------------------------------------
# Backup planen und Vorschau anzeigen
# ------------------------------------------------------------
try:
plan = backup_planen(quelle, backup_pfad)
except OSError as fehler:
print(f"FEHLER beim Lesen des Quellordners: {fehler}")
sys.exit(1)
print()
print("Backup mit Datum und Protokolldatei")
print("------------------------------------")
print(f"Testmodus: {'JA' if testmodus else 'NEIN'}")
print(f"Quelle: {quelle}")
print(f"Backup-Hauptordner: {backup_pfad}")
print(f"Neuer Backupordner: {zielordner}")
print(f"Protokolldatei: {logdatei}")
print(f"Ausgeschlossene Ordner: {', '.join(AUSGESCHLOSSENE_ORDNER)}")
print(f"Ausgeschlossene Dateien: {', '.join(AUSGESCHLOSSENE_DATEIEN)}")
print()
for relativer_ordner in plan["ordner"]:
print(f"ORDNER: {relativer_ordner}")
for datei in plan["dateien"]:
print(f"DATEI: {datei.relative_to(quelle)}")
print()
print(f"Zu sichernde Ordner: {len(plan['ordner'])}")
print(f"Zu sichernde Dateien: {len(plan['dateien'])}")
print(f"Ausgeschlossene Ordner: {len(plan['ausgeschlossene_ordner'])}")
print(f"Ausgeschlossene Dateien: {len(plan['ausgeschlossene_dateien'])}")
# ------------------------------------------------------------
# Testmodus beenden oder Echtlauf bestätigen
# ------------------------------------------------------------
if testmodus:
print()
print("TESTMODUS: Es wurden keine Ordner oder Dateien angelegt.")
print("Für den Echtmodus _test aus dem Namen der Starterdatei entfernen.")
sys.exit(0)
print()
antwort = input("Backup jetzt erstellen? Bitte JA eingeben: ")
if antwort.strip() != "JA":
print("Abgebrochen. Es wurden keine Dateien kopiert.")
sys.exit(0)
# ------------------------------------------------------------
# Backupordner anlegen und Dateien kopieren
# ------------------------------------------------------------
if zielordner.exists():
print(f"FEHLER: Der Zielordner existiert bereits: {zielordner}")
sys.exit(1)
try:
zielordner.mkdir(parents=True)
protokoll = logdatei.open("w", encoding="utf-8")
except OSError as fehler:
print(f"FEHLER: Der Backupordner konnte nicht angelegt werden: {fehler}")
sys.exit(1)
anzahl_kopiert = 0
anzahl_fehler = 0
with protokoll:
zeile_ausgeben_und_protokollieren(
protokoll,
"TOPTORIALS OFFICE - Backup-Protokoll",
)
zeile_ausgeben_und_protokollieren(protokoll, f"Zeitstempel: {zeitstempel}")
zeile_ausgeben_und_protokollieren(protokoll, f"Quelle: {quelle}")
zeile_ausgeben_und_protokollieren(protokoll, f"Ziel: {zielordner}")
zeile_ausgeben_und_protokollieren(
protokoll,
"Ausgeschlossene Ordner: "
+ ", ".join((BACKUP_HAUPTORDNER,) + AUSGESCHLOSSENE_ORDNER),
)
zeile_ausgeben_und_protokollieren(
protokoll,
"Ausgeschlossene Dateien: " + ", ".join(AUSGESCHLOSSENE_DATEIEN),
)
zeile_ausgeben_und_protokollieren(protokoll)
for relativer_ordner in plan["ordner"]:
try:
(zielordner / relativer_ordner).mkdir(parents=True, exist_ok=True)
zeile_ausgeben_und_protokollieren(
protokoll,
f"ORDNER ANGELEGT: {relativer_ordner}",
)
except OSError as fehler:
anzahl_fehler += 1
zeile_ausgeben_und_protokollieren(
protokoll,
f"FEHLER BEIM ORDNER: {relativer_ordner} - {fehler}",
)
for quelldatei in plan["dateien"]:
relativer_pfad = quelldatei.relative_to(quelle)
zieldatei = zielordner / relativer_pfad
try:
zieldatei.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(quelldatei, zieldatei)
anzahl_kopiert += 1
zeile_ausgeben_und_protokollieren(
protokoll,
f"KOPIERT: {relativer_pfad}",
)
except OSError as fehler:
anzahl_fehler += 1
zeile_ausgeben_und_protokollieren(
protokoll,
f"FEHLER BEIM KOPIEREN: {relativer_pfad} - {fehler}",
)
zeile_ausgeben_und_protokollieren(protokoll)
zeile_ausgeben_und_protokollieren(protokoll, "Zusammenfassung")
zeile_ausgeben_und_protokollieren(
protokoll,
f"Kopierte Dateien: {anzahl_kopiert}",
)
zeile_ausgeben_und_protokollieren(
protokoll,
f"Ausgeschlossene Ordner: {len(plan['ausgeschlossene_ordner'])}",
)
zeile_ausgeben_und_protokollieren(
protokoll,
f"Ausgeschlossene Dateien: {len(plan['ausgeschlossene_dateien'])}",
)
zeile_ausgeben_und_protokollieren(
protokoll,
f"Fehler: {anzahl_fehler}",
)
print()
if anzahl_fehler > 0:
print("Das Backup wurde mit Fehlern abgeschlossen.")
print(f"Bitte das Protokoll kontrollieren: {logdatei}")
sys.exit(1)
print("Backup erfolgreich abgeschlossen.")
print(f"Backupordner: {zielordner}")
print(f"Protokoll: {logdatei}")
So funktioniert die normale Variante
Nach den Einstellungen folgen die Hilfsfunktionen. Danach werden Testmodus, Quelle, Zeitstempel, Backupordner und Protokollpfad vorbereitet. backup_planen() sammelt alle erlaubten Ordner und Dateien, ohne bereits etwas zu kopieren.
Die Vorschau zeigt jedes geplante Element und die vier Zähler. Der Testmodus endet danach sofort. Im Echtmodus prüft Python zuerst die Eingabe und danach, ob der Zielordner bereits existiert.
Beim Kopieren werden Fehler pro Ordner oder Datei aufgefangen und protokolliert. Dadurch kann der Lauf die übrigen Elemente weiter bearbeiten. Sind Fehler aufgetreten, endet das Skript mit dem Rückgabecode 1; bei einem vollständig erfolgreichen Lauf bleibt es bei 0.
Starter-Batchdatei für Variante 1
Dateiname im Testmodus: 009_backup_mit_datum_mit_log_starter_batch_test.bat
@echo off
setlocal DisableDelayedExpansion
REM ============================================================
REM TOPTORIALS OFFICE - Python Grundkurs
REM Teil: 009 - Backup mit Datum und Protokolldatei
REM Script: 009_backup_mit_datum_mit_log_starter_batch_test.bat
REM Beschreibung:
REM Startet das Python-Skript, das den Ordner dieser Dateien in
REM einem datierten Backupordner mit Protokolldatei sichert.
REM Autor: Chris
REM YouTube: https://www.youtube.com/@TOPTORIALS-OFFICE
REM Webseite: https://www.toptorials.com
REM Version: Grundkurs 1.0
REM Stand: 2026-09-12
REM
REM Hinweise:
REM - Benoetigt Python 3, erreichbar ueber den Python Launcher py.
REM - Starter und Python-Datei muessen im gleichen Ordner liegen.
REM - Solange _test im Starternamen steht, wird --test uebergeben.
REM - Im Testmodus werden keine Ordner oder Dateien angelegt.
REM - Fuer den Echtmodus _test aus dem Starternamen entfernen.
REM - Das Python-Skript verlangt dann die Bestaetigung mit JA.
REM - pause haelt das Fenster nach der Ausfuehrung offen.
REM - Zuerst mit Testdaten arbeiten und wichtige Daten sichern.
REM - Die Nutzung erfolgt auf eigene Verantwortung.
REM ============================================================
chcp 65001 >nul
REM In den Ordner wechseln, in dem Starter und Python-Skript liegen.
cd /d "%~dp0"
if errorlevel 1 (
echo FEHLER: Der Skriptordner konnte nicht geoeffnet werden.
pause
exit /b 1
)
REM Pruefen, ob der eigene Dateiname den Text "_test" enthaelt.
echo("%~n0" | find /i "_test" >nul
if not errorlevel 1 (
py "009_backup_mit_datum_mit_log.py" --test
REM Alternative, falls py nicht funktioniert:
REM python "009_backup_mit_datum_mit_log.py" --test
) else (
py "009_backup_mit_datum_mit_log.py"
REM Alternative, falls py nicht funktioniert:
REM python "009_backup_mit_datum_mit_log.py"
)
pause
So funktioniert der Starter
setlocal DisableDelayedExpansion begrenzt Änderungen an der Batch-Umgebung auf den aktuellen Starter und deaktiviert die verzögerte Variablenerweiterung. Dadurch werden Ausrufezeichen in übergebenen Texten nicht als verzögerte Variablen interpretiert.
chcp 65001 schaltet die Konsole auf UTF-8. cd /d "%~dp0" wechselt in den Ordner der Batchdatei. %~n0 liefert ihren Namen ohne Dateiendung. find /i "_test" sucht darin ohne Beachtung der Groß- und Kleinschreibung nach dem Testzusatz.
Wurde _test gefunden, startet der Launcher das Python-Skript mit --test. Andernfalls fehlt dieser Schalter und Python arbeitet nach der Sicherheitsabfrage im Echtmodus. pause hält das Fenster am Ende offen.
Variante 2: Projektordner über „Senden an“ sichern
SendTo-Verknüpfung einrichten
- Drücke
Windows-Taste + R. - Gib
shell:sendtoein und bestätige. - Erstelle dort eine Verknüpfung zur SendTo-Starter-Batchdatei.
- Gib der Verknüpfung einen verständlichen Namen, beispielsweise
Python 009 – Backup testen.
Für den Echtmodus entfernst du _test aus dem tatsächlichen Namen der Batchdatei. Aktualisiere die vorhandene Verknüpfung oder lege eine zweite Verknüpfung zur Echtmodus-Datei an.
So wird die SendTo-Variante angewendet
Markiere im Windows-Explorer einen oder mehrere Projektordner. Öffne das Kontextmenü und wähle über Senden an die eingerichtete Verknüpfung. Dateien und ungültige Pfade werden mit einer Meldung übersprungen.
Jeder gültige Projektordner erhält einen eigenen Backupplan und später einen eigenen datierten Backupordner mit Protokolldatei.
Vollständiges Python-Skript
Dateiname: 009_sendto_backup_mit_datum_mit_log.py
# ============================================================
# TOPTORIALS OFFICE - Python Grundkurs
# Teil: 009 - Backup mit Datum und Protokolldatei, SendTo
# Script: 009_sendto_backup_mit_datum_mit_log.py
# Beschreibung:
# Sichert einen oder mehrere übergebene Ordner jeweils in einem
# datierten Backupordner und erstellt eigene Protokolldateien.
# Autor: Chris
# YouTube: https://www.youtube.com/@TOPTORIALS-OFFICE
# Webseite: https://www.toptorials.com
# Version: Grundkurs 1.0
# Stand: 2026-09-12
#
# Hinweise:
# - Benötigt Python 3.
# - Die Ordner werden über "Senden an" aus dem Explorer übergeben.
# - Mit --test wird nur angezeigt, was gesichert würde.
# - Im Testmodus werden keine Ordner oder Dateien angelegt.
# - Ohne --test erfolgt die Sicherung nach Bestätigung mit JA.
# - Der Ordner "Backups" wird nie in ein Backup aufgenommen.
# - Mehrfach übergebene Quellordner werden nur einmal verarbeitet.
# - Dateien und ungültige Pfade werden übersprungen.
# - Symbolische Links und Windows-Junctions werden nicht verfolgt.
# - Zuerst mit Testdaten arbeiten und wichtige Daten zusätzlich sichern.
# - Die Nutzung erfolgt auf eigene Verantwortung.
# ============================================================
from datetime import datetime
from fnmatch import fnmatchcase
import os
from pathlib import Path
import shutil
import sys
# ------------------------------------------------------------
# Einstellungen
# ------------------------------------------------------------
BACKUP_HAUPTORDNER = "Backups"
AUSGESCHLOSSENE_ORDNER = (
"Temp",
"Cache",
"Alte Dateien",
)
AUSGESCHLOSSENE_DATEIEN = (
"desktop.ini",
"thumbs.db",
"*.tmp",
"*.log",
"*.bak",
)
# ------------------------------------------------------------
# Hilfsfunktionen
# ------------------------------------------------------------
def pfad_schluessel(pfad):
"""Erzeugt einen Vergleichswert für einen vollständigen Pfad."""
return os.path.normcase(os.path.abspath(str(pfad))).casefold()
def ist_verzeichnisverknuepfung(pfad):
"""Erkennt symbolische Links und, falls verfügbar, Junctions."""
try:
if pfad.is_symlink():
return True
if hasattr(pfad, "is_junction") and pfad.is_junction():
return True
except OSError:
return True
return False
def ist_datei_ausgeschlossen(dateiname):
"""Prüft einen Dateinamen gegen alle ausgeschlossenen Muster."""
name = dateiname.casefold()
return any(
fnmatchcase(name, muster.casefold())
for muster in AUSGESCHLOSSENE_DATEIEN
)
def walk_fehler_ausloesen(fehler):
"""Gibt einen Lesefehler von os.walk() an den Aufrufer weiter."""
raise fehler
def backup_planen(quelle, backup_pfad):
"""Ermittelt alle Ordner und Dateien, die gesichert werden sollen."""
ordner = []
dateien = []
ausgeschlossene_ordner = []
ausgeschlossene_dateien = []
ausgeschlossene_ordnernamen = {
name.casefold() for name in AUSGESCHLOSSENE_ORDNER
}
backup_schluessel = pfad_schluessel(backup_pfad)
for aktueller_text, unterordner, dateinamen in os.walk(
quelle,
topdown=True,
onerror=walk_fehler_ausloesen,
followlinks=False,
):
aktueller_ordner = Path(aktueller_text)
relativer_ordner = aktueller_ordner.relative_to(quelle)
if relativer_ordner != Path("."):
ordner.append(relativer_ordner)
erlaubte_unterordner = []
for ordnername in sorted(unterordner, key=str.casefold):
ordnerpfad = aktueller_ordner / ordnername
ausgeschlossen = (
ordnername.casefold() in ausgeschlossene_ordnernamen
or pfad_schluessel(ordnerpfad) == backup_schluessel
or ist_verzeichnisverknuepfung(ordnerpfad)
)
if ausgeschlossen:
ausgeschlossene_ordner.append(ordnerpfad)
else:
erlaubte_unterordner.append(ordnername)
unterordner[:] = erlaubte_unterordner
for dateiname in sorted(dateinamen, key=str.casefold):
dateipfad = aktueller_ordner / dateiname
if ist_datei_ausgeschlossen(dateiname) or dateipfad.is_symlink():
ausgeschlossene_dateien.append(dateipfad)
else:
dateien.append(dateipfad)
return {
"ordner": ordner,
"dateien": dateien,
"ausgeschlossene_ordner": ausgeschlossene_ordner,
"ausgeschlossene_dateien": ausgeschlossene_dateien,
}
def zeile_ausgeben_und_protokollieren(protokoll, text=""):
"""Gibt eine Zeile aus und schreibt sie zusätzlich ins Protokoll."""
print(text)
protokoll.write(text + "\n")
protokoll.flush()
def backup_ausfuehren(plan):
"""Führt einen vorbereiteten Backupplan aus."""
quelle = plan["quelle"]
zielordner = plan["ziel"]
logdatei = plan["protokoll"]
inhalt = plan["inhalt"]
if zielordner.exists():
print(f"FEHLER: Der Zielordner existiert bereits: {zielordner}")
return False
try:
zielordner.mkdir(parents=True)
protokoll = logdatei.open("w", encoding="utf-8")
except OSError as fehler:
print(f"FEHLER: Der Backupordner konnte nicht angelegt werden: {fehler}")
return False
anzahl_kopiert = 0
anzahl_fehler = 0
with protokoll:
zeile_ausgeben_und_protokollieren(
protokoll,
"TOPTORIALS OFFICE - Backup-Protokoll",
)
zeile_ausgeben_und_protokollieren(
protokoll,
f"Zeitstempel: {plan['zeitstempel']}",
)
zeile_ausgeben_und_protokollieren(protokoll, f"Quelle: {quelle}")
zeile_ausgeben_und_protokollieren(protokoll, f"Ziel: {zielordner}")
zeile_ausgeben_und_protokollieren(
protokoll,
"Ausgeschlossene Ordner: "
+ ", ".join((BACKUP_HAUPTORDNER,) + AUSGESCHLOSSENE_ORDNER),
)
zeile_ausgeben_und_protokollieren(
protokoll,
"Ausgeschlossene Dateien: " + ", ".join(AUSGESCHLOSSENE_DATEIEN),
)
zeile_ausgeben_und_protokollieren(protokoll)
for relativer_ordner in inhalt["ordner"]:
try:
(zielordner / relativer_ordner).mkdir(
parents=True,
exist_ok=True,
)
zeile_ausgeben_und_protokollieren(
protokoll,
f"ORDNER ANGELEGT: {relativer_ordner}",
)
except OSError as fehler:
anzahl_fehler += 1
zeile_ausgeben_und_protokollieren(
protokoll,
f"FEHLER BEIM ORDNER: {relativer_ordner} - {fehler}",
)
for quelldatei in inhalt["dateien"]:
relativer_pfad = quelldatei.relative_to(quelle)
zieldatei = zielordner / relativer_pfad
try:
zieldatei.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(quelldatei, zieldatei)
anzahl_kopiert += 1
zeile_ausgeben_und_protokollieren(
protokoll,
f"KOPIERT: {relativer_pfad}",
)
except OSError as fehler:
anzahl_fehler += 1
zeile_ausgeben_und_protokollieren(
protokoll,
f"FEHLER BEIM KOPIEREN: {relativer_pfad} - {fehler}",
)
zeile_ausgeben_und_protokollieren(protokoll)
zeile_ausgeben_und_protokollieren(protokoll, "Zusammenfassung")
zeile_ausgeben_und_protokollieren(
protokoll,
f"Kopierte Dateien: {anzahl_kopiert}",
)
zeile_ausgeben_und_protokollieren(
protokoll,
f"Ausgeschlossene Ordner: {len(inhalt['ausgeschlossene_ordner'])}",
)
zeile_ausgeben_und_protokollieren(
protokoll,
f"Ausgeschlossene Dateien: {len(inhalt['ausgeschlossene_dateien'])}",
)
zeile_ausgeben_und_protokollieren(
protokoll,
f"Fehler: {anzahl_fehler}",
)
if anzahl_fehler > 0:
print(f"Backup mit Fehlern abgeschlossen: {quelle}")
print(f"Bitte das Protokoll kontrollieren: {logdatei}")
return False
print(f"Backup erfolgreich abgeschlossen: {quelle}")
print(f"Backupordner: {zielordner}")
print(f"Protokoll: {logdatei}")
return True
# ------------------------------------------------------------
# Argumente und Testmodus auswerten
# ------------------------------------------------------------
argumente = sys.argv[1:]
testmodus = "--test" in argumente
pfad_argumente = [argument for argument in argumente if argument != "--test"]
if not pfad_argumente:
print("FEHLER: Es wurden keine Ordner übergeben.")
print("Bitte einen oder mehrere Ordner über 'Senden an' starten.")
sys.exit(1)
# ------------------------------------------------------------
# Gültige Quellordner sammeln
# ------------------------------------------------------------
quellordner = []
bereits_erfasste_ordner = set()
for pfad_text in pfad_argumente:
try:
element = Path(pfad_text)
if not element.exists():
print(f"ÜBERSPRUNGEN: Pfad nicht gefunden: {pfad_text}")
continue
if ist_verzeichnisverknuepfung(element):
print(f"ÜBERSPRUNGEN: Verzeichnisverknüpfung: {element}")
continue
element = element.resolve()
if not element.is_dir():
print(f"ÜBERSPRUNGEN: Kein Ordner: {element}")
continue
schluessel = pfad_schluessel(element)
if schluessel not in bereits_erfasste_ordner:
bereits_erfasste_ordner.add(schluessel)
quellordner.append(element)
except OSError as fehler:
print(f"ÜBERSPRUNGEN: {pfad_text} - {fehler}")
if not quellordner:
print("FEHLER: Es wurde kein gültiger Quellordner gefunden.")
sys.exit(1)
# ------------------------------------------------------------
# Für jeden Quellordner einen Backupplan erstellen
# ------------------------------------------------------------
zeitstempel = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
plaene = []
anzahl_planungsfehler = 0
for quelle in quellordner:
backup_pfad = quelle / BACKUP_HAUPTORDNER
zielordner = backup_pfad / f"Backup_{zeitstempel}"
logdatei = zielordner / f"backup_protokoll_{zeitstempel}.log"
try:
inhalt = backup_planen(quelle, backup_pfad)
plaene.append(
{
"quelle": quelle,
"backup_pfad": backup_pfad,
"ziel": zielordner,
"protokoll": logdatei,
"zeitstempel": zeitstempel,
"inhalt": inhalt,
}
)
except OSError as fehler:
print(f"FEHLER beim Lesen von '{quelle}': {fehler}")
anzahl_planungsfehler += 1
if not plaene:
print("FEHLER: Es konnte kein Backupplan erstellt werden.")
sys.exit(1)
# ------------------------------------------------------------
# Alle Backuppläne anzeigen
# ------------------------------------------------------------
print()
print("SendTo-Backup mit Datum und Protokolldatei")
print("------------------------------------------")
print(f"Testmodus: {'JA' if testmodus else 'NEIN'}")
print(f"Anzahl der Quellordner: {len(plaene)}")
print(f"Ausgeschlossene Ordner: {', '.join(AUSGESCHLOSSENE_ORDNER)}")
print(f"Ausgeschlossene Dateien: {', '.join(AUSGESCHLOSSENE_DATEIEN)}")
print()
for nummer, plan in enumerate(plaene, start=1):
inhalt = plan["inhalt"]
print(f"Backup {nummer} von {len(plaene)}")
print(f"Quelle: {plan['quelle']}")
print(f"Ziel: {plan['ziel']}")
print(f"Protokoll: {plan['protokoll']}")
print(f"Ordner: {len(inhalt['ordner'])}")
print(f"Dateien: {len(inhalt['dateien'])}")
for relativer_ordner in inhalt["ordner"]:
print(f" ORDNER: {relativer_ordner}")
for datei in inhalt["dateien"]:
print(f" DATEI: {datei.relative_to(plan['quelle'])}")
print()
# ------------------------------------------------------------
# Testmodus beenden oder Echtlauf bestätigen
# ------------------------------------------------------------
if testmodus:
print("TESTMODUS: Es wurden keine Ordner oder Dateien angelegt.")
print("Für den Echtmodus _test aus dem Namen der Starterdatei entfernen.")
sys.exit(1 if anzahl_planungsfehler > 0 else 0)
antwort = input("Alle angezeigten Backups jetzt erstellen? Bitte JA eingeben: ")
if antwort.strip() != "JA":
print("Abgebrochen. Es wurden keine Dateien kopiert.")
sys.exit(0)
# ------------------------------------------------------------
# Backuppläne ausführen und Ergebnis zusammenfassen
# ------------------------------------------------------------
erfolgreich = 0
fehlgeschlagen = anzahl_planungsfehler
for plan in plaene:
print()
print(f"Bearbeite: {plan['quelle']}")
if backup_ausfuehren(plan):
erfolgreich += 1
else:
fehlgeschlagen += 1
print()
print("Zusammenfassung")
print("---------------")
print(f"Erfolgreich: {erfolgreich}")
print(f"Fehlgeschlagen: {fehlgeschlagen}")
if fehlgeschlagen > 0:
sys.exit(1)
sys.exit(0)
So funktioniert die SendTo-Variante
sys.argv[1:] enthält alle Argumente nach dem Namen des Python-Skripts. Der Testschalter wird zunächst erkannt und anschließend aus den eigentlichen Pfadargumenten entfernt.
argumente = sys.argv[1:]
testmodus = "--test" in argumente
pfad_argumente = [argument for argument in argumente if argument != "--test"]
Die Pfade werden auf Existenz, Verzeichnisverknüpfung und Ordnertyp geprüft. Das Set bereits_erfasste_ordner merkt sich einen normalisierten Vergleichsschlüssel. Derselbe Projektordner wird dadurch auch dann nur einmal verarbeitet, wenn er mehrfach übergeben wurde.
Für jede gültige Quelle entsteht ein Dictionary mit Quelle, Backup-Hauptordner, Ziel, Protokollpfad, Zeitstempel und Inhalt. Die Liste plaene sammelt diese Dictionaries.
enumerate(plaene, start=1) liefert beim Anzeigen gleichzeitig jeden Plan und eine laufende Nummer ab 1. Erst nachdem alle Pläne sichtbar sind, folgt im Echtmodus eine einzige Sicherheitsabfrage für den gesamten Durchlauf.
backup_ausfuehren() übernimmt anschließend jeden Plan. Die Zähler erfolgreich und fehlgeschlagen ergeben am Ende eine gemeinsame Zusammenfassung.
Starter-Batchdatei für Variante 2
Dateiname im Testmodus: 009_sendto_backup_mit_datum_mit_log_starter_batch_test.bat
@echo off
setlocal DisableDelayedExpansion
REM ============================================================
REM TOPTORIALS OFFICE - Python Grundkurs
REM Teil: 009 - Backup mit Datum und Protokolldatei, SendTo
REM Script: 009_sendto_backup_mit_datum_mit_log_starter_batch_test.bat
REM Beschreibung:
REM Startet die SendTo-Variante und reicht alle im Explorer
REM ausgewaehlten Ordner an das zugehoerige Python-Skript weiter.
REM Autor: Chris
REM YouTube: https://www.youtube.com/@TOPTORIALS-OFFICE
REM Webseite: https://www.toptorials.com
REM Version: Grundkurs 1.0
REM Stand: 2026-09-12
REM
REM Hinweise:
REM - Benoetigt Python 3, erreichbar ueber den Python Launcher py.
REM - Starter und Python-Datei muessen im gleichen Ordner liegen.
REM - Eine Verknuepfung zu diesem Starter in "Senden an" ablegen.
REM - Einen oder mehrere Ordner markieren und dorthin senden.
REM - Solange _test im Starternamen steht, wird --test uebergeben.
REM - Der Anzeigename der Verknuepfung steuert den Testmodus nicht.
REM - Fuer den Echtmodus _test aus dem Starternamen entfernen.
REM - Die SendTo-Verknuepfung danach gegebenenfalls aktualisieren.
REM - Das Python-Skript verlangt dann die Bestaetigung mit JA.
REM - pause haelt das Fenster nach der Ausfuehrung offen.
REM - Zuerst mit Testdaten arbeiten und wichtige Daten sichern.
REM - Die Nutzung erfolgt auf eigene Verantwortung.
REM ============================================================
chcp 65001 >nul
REM In den Ordner wechseln, in dem Starter und Python-Skript liegen.
cd /d "%~dp0"
if errorlevel 1 (
echo FEHLER: Der Skriptordner konnte nicht geoeffnet werden.
pause
exit /b 1
)
REM Pruefen, ob der eigene Dateiname den Text "_test" enthaelt.
echo("%~n0" | find /i "_test" >nul
if not errorlevel 1 (
py "009_sendto_backup_mit_datum_mit_log.py" --test %*
REM Alternative, falls py nicht funktioniert:
REM python "009_sendto_backup_mit_datum_mit_log.py" --test %*
) else (
py "009_sendto_backup_mit_datum_mit_log.py" %*
REM Alternative, falls py nicht funktioniert:
REM python "009_sendto_backup_mit_datum_mit_log.py" %*
)
pause
Der entscheidende Unterschied im SendTo-Starter
Der Python-Aufruf endet zusätzlich mit:
%*
%* steht in einer Batchdatei für sämtliche übergebenen Argumente. Der Windows-Explorer übergibt hier die markierten Pfade. Der Starter reicht sie unverändert an Python weiter. Dort erscheinen sie in sys.argv.
Typische Meldungen
Testmodus
TESTMODUS: Es wurden keine Ordner oder Dateien angelegt.
Erfolgreiches Backup
Backup erfolgreich abgeschlossen.
Backupordner: C:\Projekte\MeinProjekt\Backups\Backup_2026-09-12_18-30-45
Protokoll: C:\Projekte\MeinProjekt\Backups\Backup_2026-09-12_18-30-45\backup_protokoll_2026-09-12_18-30-45.log
Datei wurde an SendTo übergeben
ÜBERSPRUNGEN: Kein Ordner: C:\Projekte\Hinweis.txt
Pfad wurde nicht gefunden
ÜBERSPRUNGEN: Pfad nicht gefunden: C:\NichtVorhanden
Backup mit Fehlern
Das Backup wurde mit Fehlern abgeschlossen.
Bitte das Protokoll kontrollieren: ...
Typische Fehler und mögliche Ursachen
Der Befehl py wurde nicht gefunden
Prüfe mit py --version, ob der Python Launcher verfügbar ist. Falls Python nur über python gestartet werden kann, stehen die alternativen Aufrufe bereits als Kommentare in den Batchdateien.
Das Python-Skript wird nicht gefunden
Prüfe, ob Starter und Python-Datei im selben Ordner liegen und der aufgerufene Dateiname exakt stimmt. Zusätze wie (1) oder (2), die beim mehrfachen Herunterladen entstehen können, gehören nicht zu den vorgesehenen Namen.
Der Starter zeigt Testmodus: NEIN
Im tatsächlichen Namen der Starter-Batchdatei fehlt _test. Der Anzeigename einer SendTo-Verknüpfung aktiviert den Testmodus nicht.
Bei SendTo entsteht das Backup im Skriptordner
Prüfe, ob wirklich die SendTo-Version des Python-Skripts und der zugehörige SendTo-Starter verwendet werden. Die normale Version sichert absichtlich ihren eigenen Skriptordner.
Im SendTo-Menü startet gar nichts oder Python wird nicht gefunden
Lege im Ordner Senden an eine Verknüpfung zur Batchdatei ab. Kopiere die Batchdatei nicht allein dorthin, denn dann fehlt ihr das daneben erwartete Python-Skript.
Bestimmte Dateien fehlen im Backup
Prüfe AUSGESCHLOSSENE_DATEIEN. Die Muster *.tmp, *.log und *.bak lassen entsprechende Dateien unabhängig von ihrer Groß- und Kleinschreibung aus.
Bestimmte Ordner fehlen im Backup
Prüfe AUSGESCHLOSSENE_ORDNER. Zusätzlich werden der Backup-Hauptordner sowie symbolische Links und Windows-Junctions grundsätzlich nicht durchlaufen.
Ein Zielordner mit demselben Zeitstempel existiert bereits
Da der Zeitstempel Sekunden enthält, ist dies selten. Das Skript verwendet einen vorhandenen Zielordner nicht erneut und überschreibt ihn nicht, sondern bricht den betreffenden Backupplan mit einer Fehlermeldung ab.
Einzelne Dateien lassen sich nicht kopieren
Mögliche Ursachen sind fehlende Rechte, eine Dateisperre, ein zu langer Pfad oder ein Datenträgerfehler. Das Skript protokolliert den Fehler, bearbeitet die übrigen Elemente weiter und meldet den Lauf anschließend als fehlerhaft.
Wichtige Python-Begriffe aus diesem Tutorial
datetime.now()- Liefert das aktuelle lokale Datum und die aktuelle Uhrzeit.
strftime()- Formatiert Datum und Uhrzeit nach einem vorgegebenen Muster als Text.
Path(__file__).resolve().parent- Liefert den aufgelösten Ordnerpfad des ausgeführten Python-Skripts.
os.walk()- Durchläuft einen Verzeichnisbaum und liefert den aktuellen Ordner, seine Unterordner und seine Dateien.
topdown=True- Ermöglicht es, Unterordner vor dem Betreten aus der Liste von
os.walk()zu entfernen. fnmatchcase()- Vergleicht einen Dateinamen mit einem Muster wie
*.log. casefold()- Erzeugt einen Vergleichswert, bei dem Groß- und Kleinschreibung keine Rolle spielen.
set()- Speichert eindeutige Vergleichsschlüssel und verhindert in SendTo eine doppelte Verarbeitung.
Dictionary- Speichert zusammengehörige Werte unter Namen wie
quelle,zielundprotokoll. relative_to()- Ermittelt den Pfad eines Elements relativ zum Quellordner.
mkdir(parents=True)- Legt einen Ordner und bei Bedarf auch noch fehlende übergeordnete Ordner an.
shutil.copy2()- Kopiert eine Datei und versucht, ihre Metadaten zu übernehmen.
with- Verwaltet hier die geöffnete Protokolldatei und schließt sie automatisch.
tryundexcept OSError- Fangen Fehler bei Datei- und Ordnerzugriffen kontrolliert ab.
sys.argv[1:]- Enthält die Argumente, die nach dem Skriptnamen an Python übergeben wurden.
enumerate(..., start=1)- Liefert Elemente einer Schleife zusammen mit einer laufenden Nummer ab eins.
sys.exit(0)undsys.exit(1)- Beenden das Programm mit einem Erfolgs- beziehungsweise Fehlercode.
Wichtige Batch-Begriffe aus diesem Tutorial
setlocal DisableDelayedExpansion- Begrenzt Umgebungsänderungen auf den Starter und verhindert die verzögerte Auswertung von Ausrufezeichen.
%~dp0- Liefert Laufwerk und Ordner der ausgeführten Starter-Batchdatei.
cd /d- Wechselt in diesen Ordner und bei Bedarf auch auf das passende Laufwerk.
%~n0- Liefert den Namen der Batchdatei ohne Ordner und Dateiendung.
find /i- Sucht ohne Beachtung der Groß- und Kleinschreibung nach
_test. if not errorlevel 1- Prüft hier, ob
findden gesuchten Text gefunden hat. %*- Reicht alle an den SendTo-Starter übergebenen Pfade an Python weiter.
pause- Hält das Konsolenfenster bis zum nächsten Tastendruck geöffnet.
Was zeichnet diese Grundkurs-Version aus?
- Sie benötigt nur Python 3 und keine zusätzlichen Pakete.
- Sie plant den gesamten Lauf, bevor etwas kopiert wird.
- Sie bietet einen echten Testmodus ohne Schreibzugriffe.
- Sie verlangt im Echtmodus eine zusätzliche Bestätigung.
- Sie übernimmt Dateien, Unterordner und leere Ordner.
- Sie schließt vorbereitete Ordner, Dateimuster und den Backup-Hauptordner aus.
- Sie verfolgt keine symbolischen Verzeichnislinks oder Junctions.
- Sie schreibt für jedes Backup eine eigene UTF-8-Protokolldatei.
- Sie verarbeitet über SendTo mehrere Projektordner und dedupliziert sie.
Was ist bewusst nicht enthalten?
Die Skripte erstellen vollständige, unkomprimierte Kopien. Sie komprimieren oder verschlüsseln keine Dateien, löschen keine alten Sicherungen und vergleichen keine Prüfsummen. Die Protokolldatei dokumentiert den Ablauf, ersetzt aber keine zusätzliche Integritätsprüfung.
Eine automatische Aufbewahrungsstrategie und ein separates Backupziel auf einem anderen Datenträger können in späteren, weiterführenden Lösungen ergänzt werden.
Was haben wir in diesem Teil gelernt?
Mit datetime.now().strftime() erzeugen wir einen einheitlichen Zeitstempel für Backupordner und Protokolldatei. Path setzt die benötigten Pfade übersichtlich zusammen.
os.walk() durchläuft den gesamten Projektordner. Durch topdown=True und die Änderung von unterordner[:] werden ausgeschlossene Verzeichnisse nicht betreten. fnmatchcase() und casefold() prüfen Dateimuster ohne Beachtung der Groß- und Kleinschreibung.
Der Backupplan speichert erlaubte Ordner und Dateien zunächst in Listen. Im Testmodus wird dieser Plan nur angezeigt. Im Echtmodus legt mkdir() die Zielstruktur an und shutil.copy2() kopiert die Dateien nach der Bestätigung mit JA.
Die Protokolldatei hält Quelle, Ziel, Ausschlüsse, Kopiervorgänge und Fehler fest. Die normale Variante sichert den Ordner des Python-Skripts. Die SendTo-Version übernimmt dagegen einen oder mehrere Projektordner aus dem Explorer und erstellt in jeder Quelle eine eigene datierte Sicherung.
Das könnte Dich auch interessieren
Keine Einträge gefunden

