Python Grundkurs Teil 007: Zeitstempel hinzufügen und entfernen
Worum geht es in diesem Teil?
In diesem Teil des Python-Grundkurses versehen wir Dateinamen automatisch mit einem Zeitstempel und entfernen diesen bei Bedarf wieder.
Aus einer Datei wie:
Rechnung.pdf
wird beispielsweise:
2026-08-26_14-35-42_Rechnung.pdf

Python Grundkurs Zeitstempel im Dateinamen
Das verwendete Format lautet:
JJJJ-MM-TT_HH-MM-SS_Dateiname.ext
Wir erstellen vier aufeinander abgestimmte Varianten:
- Zeitstempel bei Dateien im aktuellen Ordner hinzufügen
- Zeitstempel bei Dateien im aktuellen Ordner wieder entfernen
- Zeitstempel über Senden an hinzufügen
- Zeitstempel über Senden an wieder entfernen
Die beiden normalen Varianten werden gemeinsam mit ihren Starter-Batchdateien direkt in den zu bearbeitenden Ordner kopiert. Die beiden SendTo-Varianten erhalten Dateien und Ordner dagegen als Argumente aus dem Windows-Explorer.
Was ist das Ziel?
Mit einem Zeitstempel am Anfang lassen sich Dateien chronologisch sortieren und einem bestimmten Bearbeitungszeitpunkt zuordnen. Das ist beispielsweise bei Exporten, Sicherungen, Berichten oder regelmäßig erzeugten Dateien hilfreich.
Da ein solches Präfix nicht immer dauerhaft benötigt wird, gehört zu jeder Erstellen-Variante eine passende Entfernen-Variante. Alle vier Skripte unterstützen einen sicheren Testmodus, planen Änderungen zunächst nur vor und führen die eigentliche Umbenennung erst nach allen Prüfungen aus.
Warum ist dieser Teil wichtig?
In diesem Tutorial verbinden wir mehrere wichtige Python-Grundlagen:
datetime.now()ermittelt das aktuelle Datum und die aktuelle Uhrzeit.strftime()formatiert daraus einen einheitlichen Zeitstempel.re.compile()erstellt ein wiederverwendbares reguläres Ausdrucksmuster.Path.cwd()liefert den aktuellen Arbeitsordner.iterdir()ermittelt direkt enthaltene Dateien und Ordner.suffixliefert die Dateiendung undcasefold()ermöglicht einen Vergleich ohne Beachtung der Groß- und Kleinschreibung.- Listen mit Dictionaries speichern geplante Umbenennungen mit Quelle und Ziel.
- Sets verhindern doppelte Pfade und reservieren bereits geplante Zielnamen.
rename()führt die eigentliche Umbenennung im Echtmodus aus.sys.argvübernimmt den Testschalter und die von SendTo übergebenen Pfade.
Hinweis zur Kursreihe
Dieses Tutorial gehört zum Python-Grundkurs. Der Aufbau orientiert sich an den entsprechenden Teilen für Batch und PowerShell 7. Dadurch lassen sich die unterschiedlichen Schreibweisen und Möglichkeiten der drei Programmiersprachen besonders gut vergleichen.
Downloads zu diesem Teil
Download Python Grundkurs: 007_Zeitstempel_im_Dateinamen als ZIP-Datei.Entpacken nach:
C:\TOPTORIALS_CODE\020_Python_Grundkurs
Dadurch entsteht dieser Hauptordner:
C:\TOPTORIALS_CODE\020_Python_Grundkurs\007_Zeitstempel_im_Dateinamen
Testumgebung zum Mitmachen
Für gefahrlose Tests kann wieder die vorbereitete Testumgebung verwendet werden.
Testumgebung für Windows-Automatisierung:Download TOPTORIALS_Testdaten_Windows_Automatisierung_V001.zip
Entpacken nach:
C:\TOPTORIALS_CODE\1000_TESTBEREICH
Für Python befindet sich der passende Testbereich anschließend unter:
C:\TOPTORIALS_CODE\1000_TESTBEREICH\TOPTORIALS_Testdaten_Windows_Automatisierung_Python_V001\007_Zeitstempel_im_Dateinamen
Sicherheitshinweis
Wichtig:Teste Skripte zum Umbenennen zuerst immer mit Kopien unwichtiger Dateien. Die mitgelieferten Starter-Batchdateien enthalten im Dateinamen zunächst _test. Dadurch wird nur angezeigt und protokolliert, was Python umbenennen würde.
Für den Echtmodus wird _test aus dem Namen der tatsächlichen Starter-Batchdatei entfernt. Zusätzlich muss die Sicherheitsabfrage mit JA bestätigt werden.
Verwende bei wichtigen Dateien zusätzlich eine aktuelle Datensicherung. Die Nutzung der Skripte erfolgt auf eigene Verantwortung.
Voraussetzungen
- Windows 10 oder Windows 11
- installiertes Python 3 und der Python Launcher
py - Schreibrechte in den zu bearbeitenden Ordnern
- Python-Skript und passende Starter-Batchdatei bleiben jeweils zusammen
Enthaltene Ordner und Dateien
007_Zeitstempel_im_Dateinamen
│
├── 01_Zeitstempel_im_Dateinamen
│ ├── 007_zeitstempel_im_dateinamen.py
│ └── 007_zeitstempel_im_dateinamen_starter_batch_test.bat
│
├── 02_Zeitstempel_aus_Dateinamen_entfernen
│ ├── 007_zeitstempel_aus_dateinamen_entfernen.py
│ └── 007_zeitstempel_aus_dateinamen_entfernen_starter_batch_test.bat
│
├── 03_SendTo_Zeitstempel_im_Dateinamen
│ ├── 007_sendto_zeitstempel_im_dateinamen.py
│ └── 007_sendto_zeitstempel_im_dateinamen_starter_batch_test.bat
│
└── 04_SendTo_Zeitstempel_aus_Dateinamen_entfernen
├── 007_sendto_zeitstempel_aus_dateinamen_entfernen.py
└── 007_sendto_zeitstempel_aus_dateinamen_entfernen_starter_batch_test.bat
Das Zeitstempelformat
Der Zeitstempel wird genau einmal pro Skriptdurchlauf erzeugt:
zeitstempel = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
datetime.now() liefert das aktuelle Datum und die aktuelle Uhrzeit. strftime() wandelt diese Werte anhand des angegebenen Formattextes in eine Zeichenkette um.
%Ysteht für das vierstellige Jahr.%msteht für den zweistelligen Monat.%dsteht für den zweistelligen Tag.%Hsteht für die Stunde im 24-Stunden-Format.%Msteht für die Minute.%Ssteht für die Sekunde.
Alle Dateien eines Durchlaufs erhalten denselben Zeitstempel. Die Reihenfolge Jahr, Monat, Tag, Stunde, Minute und Sekunde sorgt dafür, dass die Dateinamen auch alphabetisch chronologisch sortiert werden.
Das reguläre Ausdrucksmuster
Die Erstellen-Varianten erkennen mit diesem Muster, ob ein Dateiname bereits mit einem Zeitstempel beginnt:
zeitstempel_muster = re.compile(
r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}_"
)
Das Zirkumflex ^ verankert die Suche am Anfang des Dateinamens. \d steht für eine Ziffer und die Zahlen in geschweiften Klammern bestimmen, wie oft die Ziffer vorkommen muss.
Die Entfernen-Varianten verwenden am Ende _?. Das Fragezeichen macht den Unterstrich optional. Dadurch kann das Skript auch einen passenden Zeitstempel entfernen, wenn direkt danach kein Unterstrich steht.
Welche Dateien werden ausgelassen?
Damit die Kursdateien und mögliche Protokolle nicht selbst umbenannt werden, sind folgende Dateiendungen ausgeschlossen:
.bat, .cmd, .ps1, .py, .log
Die Verarbeitung ist außerdem nicht rekursiv. Bei einem übergebenen Ordner werden nur Dateien verarbeitet, die direkt in diesem Ordner liegen. Inhalte tieferer Unterordner bleiben unverändert.
Testmodus und Echtmodus
Jede Starter-Batchdatei prüft ihren eigenen Dateinamen. Solange darin _test vorkommt, übergibt sie das Argument --test an das Python-Skript.
- Mit
_test: Die geplanten Änderungen werden nur angezeigt. - Ohne
_test: Nach der Bestätigung mitJAwerden die Dateien wirklich umbenannt.
Python prüft den Testmodus mit:
testmodus = "--test" in sys.argv
Alle gültigen Umbenennungen werden zunächst in der Liste plaene gesammelt. Jeder Plan ist ein Dictionary mit Quelle, altem Namen, neuem Namen und Zielpfad. Erst eine zweite Schleife zeigt die Pläne an oder führt sie aus.
Ausgabe, Logdatei und Zwischenablage
In allen vier Python-Skripten stehen dieselben vier Ausgabemöglichkeiten bereit:
- nur Bildschirmausgabe,
- Bildschirmausgabe und Logdatei,
- Bildschirmausgabe und Zwischenablage oder
- Bildschirmausgabe, Logdatei und Zwischenablage.
Die gewünschte Kombination wird mit diesen beiden Variablen eingestellt:
logdatei_erstellen = False
zwischenablage_verwenden = False
Für eine andere Variante werden genau die beiden zugehörigen Zeilen aktiviert und die bisherige Variante auskommentiert. Die Bildschirmausgabe bleibt immer erhalten.
Variante 1: Zeitstempel im aktuellen Ordner erstellen
So wird die Variante angewendet
Kopiere den Inhalt des Ordners 01_Zeitstempel_im_Dateinamen in den Ordner, dessen Dateien einen Zeitstempel erhalten sollen. Python-Skript und Starter-Batchdatei müssen gemeinsam dort liegen.
Mein_Arbeitsordner
├── 007_zeitstempel_im_dateinamen.py
├── 007_zeitstempel_im_dateinamen_starter_batch_test.bat
├── Rechnung.pdf
├── Bericht.docx
└── Foto.jpg
Starte zuerst die Starter-Batchdatei mit _test. Wenn die angezeigten Zielnamen stimmen, entfernst du _test nur aus dem Dateinamen der Starter-Batchdatei und bestätigst den Echtlauf mit JA.
Danach können die Dateien beispielsweise so aussehen:
2026-08-26_14-35-42_Rechnung.pdf
2026-08-26_14-35-42_Bericht.docx
2026-08-26_14-35-42_Foto.jpg
Vollständiges Python-Skript
# ============================================================
# TOPTORIALS OFFICE - Python Grundkurs
# Script: 007_zeitstempel_im_dateinamen.py
# Beschreibung: Fügt Dateien im aktuellen Ordner einen Zeitstempel hinzu.
# Autor: Chris
# YouTube: https://www.youtube.com/@TOPTORIALS-OFFICE
# Webseite: https://www.toptorials.com
# Version: 1.0
# Stand: 2026-08-26
# ============================================================
from datetime import datetime
from pathlib import Path
import re
import subprocess
import sys
print()
basis_ordner = Path.cwd()
testmodus = "--test" in sys.argv
zeitstempel = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
zeitstempel_muster = re.compile(
r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}_"
)
ausgeschlossene_endungen = (".bat", ".cmd", ".ps1", ".py", ".log")
# ------------------------------------------------------------
# Welche zusätzliche Ausgabe soll erstellt werden?
# Wichtig: Immer nur EINE der folgenden Varianten aktiv lassen.
# ------------------------------------------------------------
# Variante 1: Nur Bildschirmausgabe
logdatei_erstellen = False
zwischenablage_verwenden = False
# Variante 2: Logdatei erstellen
# logdatei_erstellen = True
# zwischenablage_verwenden = False
# Variante 3: Nur in die Zwischenablage kopieren
# logdatei_erstellen = False
# zwischenablage_verwenden = True
# Variante 4: Logdatei erstellen und in die Zwischenablage kopieren
# logdatei_erstellen = True
# zwischenablage_verwenden = True
# ------------------------------------------------------------
# Modus, Zähler, Protokoll und Pläne vorbereiten
# ------------------------------------------------------------
if testmodus:
modus_text = "TESTMODUS"
else:
modus_text = "ECHTMODUS"
anzahl_umbenannt = 0
anzahl_uebersprungen = 0
anzahl_fehler = 0
protokoll = []
plaene = []
protokoll.append(
"Dateien im aktuellen Ordner werden mit einem Zeitstempel umbenannt."
)
protokoll.append(f"Modus: {modus_text}")
protokoll.append(f"Ordner: {basis_ordner}")
protokoll.append(f"Zeitstempel: {zeitstempel}")
protokoll.append(
"Ausgeschlossene Endungen: " + ", ".join(ausgeschlossene_endungen)
)
protokoll.append("")
print("Dateien im aktuellen Ordner werden mit einem Zeitstempel umbenannt...")
print()
print("Ordner:")
print(basis_ordner)
print()
print(f"Zeitstempel: {zeitstempel}")
print(f"Modus: {modus_text}")
print("Ausgeschlossene Endungen: " + ", ".join(ausgeschlossene_endungen))
print()
# ------------------------------------------------------------
# Direkt enthaltene Dateien erfassen
# Unterordner werden nicht durchsucht.
# ------------------------------------------------------------
dateien = []
try:
for pfad in basis_ordner.iterdir():
if pfad.is_file():
dateien.append(pfad)
except OSError as fehler:
print(
"FEHLER: Der Ordnerinhalt konnte nicht gelesen werden. "
f"Grund:\n{fehler}"
)
print()
input("Drücke Enter zum Beenden")
sys.exit()
# ------------------------------------------------------------
# Dateien prüfen und Umbenennungen planen
# ------------------------------------------------------------
for datei in dateien:
if datei.suffix.casefold() in ausgeschlossene_endungen:
meldung = (
"ÜBERSPRUNGEN:\n"
f'"{datei}"\n'
"- Dateiendung ist ausgeschlossen\n"
"---"
)
print(meldung)
protokoll.append(meldung)
anzahl_uebersprungen += 1
continue
if zeitstempel_muster.match(datei.name):
meldung = (
"ÜBERSPRUNGEN:\n"
f'"{datei}"\n'
"- Zeitstempel ist bereits vorhanden\n"
"---"
)
print(meldung)
protokoll.append(meldung)
anzahl_uebersprungen += 1
continue
neuer_name = zeitstempel + "_" + datei.name
ziel_pfad = basis_ordner / neuer_name
if ziel_pfad.exists():
meldung = (
"ÜBERSPRUNGEN:\n"
f'"{datei}"\n'
"- Zieldatei existiert bereits:\n"
f'"{ziel_pfad}"\n'
"---"
)
print(meldung)
protokoll.append(meldung)
anzahl_uebersprungen += 1
continue
plaene.append(
{
"quelle": datei,
"alter_name": datei.name,
"neuer_name": neuer_name,
"ziel": ziel_pfad,
}
)
# ------------------------------------------------------------
# Sicherheitsabfrage bei der echten Ausführung
# ------------------------------------------------------------
if not testmodus and plaene:
print()
print("ACHTUNG: Dieses Skript wird Dateien wirklich umbenennen.")
print()
antwort = input("Zum Fortfahren JA eingeben: ")
if antwort.upper() != "JA":
print()
print("Abgebrochen.")
print()
input("Drücke Enter zum Beenden")
sys.exit()
print()
# ------------------------------------------------------------
# Geplante Umbenennungen ausführen
# ------------------------------------------------------------
for plan in plaene:
if testmodus:
meldung = (
"WÜRDE UMBENENNEN:\n"
f'Quelle: "{plan["quelle"]}"\n'
f'Ziel: "{plan["ziel"]}"\n'
"---"
)
print(meldung)
protokoll.append(meldung)
anzahl_umbenannt += 1
continue
try:
plan["quelle"].rename(plan["ziel"])
meldung = (
"UMBENANNT:\n"
f'Quelle: "{plan["quelle"]}"\n'
f'Ziel: "{plan["ziel"]}"\n'
"---"
)
print(meldung)
protokoll.append(meldung)
anzahl_umbenannt += 1
except OSError as fehler:
meldung = (
"FEHLER:\n"
f'"{plan["quelle"]}"\n'
"konnte nicht umbenannt werden. Grund:\n"
f"{fehler}"
)
print(meldung)
protokoll.append(meldung)
anzahl_fehler += 1
# ------------------------------------------------------------
# Zusammenfassung
# ------------------------------------------------------------
protokoll.append("")
protokoll.append("Fertig.")
print()
print("Fertig.")
print()
if testmodus:
meldung_1 = f"Anzahl Dateien, die umbenannt würden: {anzahl_umbenannt}"
meldung_2 = f"Anzahl übersprungen: {anzahl_uebersprungen}"
meldung_3 = f"Anzahl Fehler: {anzahl_fehler}"
print(meldung_1)
print(meldung_2)
print(meldung_3)
print()
print("Es wurden keine Dateien umbenannt.")
print(
"Wenn alles richtig aussieht, entferne _test aus dem Namen "
"der Starter-Batchdatei."
)
protokoll.append(meldung_1)
protokoll.append(meldung_2)
protokoll.append(meldung_3)
protokoll.append("Es wurden keine Dateien umbenannt.")
else:
meldung_1 = f"Anzahl umbenannt: {anzahl_umbenannt}"
meldung_2 = f"Anzahl übersprungen: {anzahl_uebersprungen}"
meldung_3 = f"Anzahl Fehler: {anzahl_fehler}"
print(meldung_1)
print(meldung_2)
print(meldung_3)
protokoll.append(meldung_1)
protokoll.append(meldung_2)
protokoll.append(meldung_3)
# ------------------------------------------------------------
# Optionale Ausgabe als Logdatei und in die Zwischenablage
# ------------------------------------------------------------
protokoll_text = "\n".join(protokoll)
protokoll_zeitstempel = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
logdatei = (
basis_ordner
/ f"umbenennprotokoll_zeitstempel_setzen_{protokoll_zeitstempel}.log"
)
if logdatei_erstellen:
try:
logdatei.write_text(protokoll_text, encoding="utf-8")
print()
print("Das Protokoll wurde erstellt:")
print(logdatei)
except OSError as fehler:
print()
print(
"FEHLER: Das Protokoll konnte nicht erstellt werden. "
f"Grund: {fehler}"
)
if zwischenablage_verwenden:
try:
subprocess.run(
"clip",
input=protokoll_text,
text=True,
encoding="utf-8",
check=True,
)
print()
print("Das Protokoll wurde zusätzlich in die Zwischenablage kopiert.")
except (OSError, subprocess.CalledProcessError) as fehler:
print()
print(
"FEHLER: Das Protokoll konnte nicht kopiert werden. "
f"Grund: {fehler}"
)
print()
input("Drücke Enter zum Beenden")
So funktioniert die Erstellen-Variante
Path.cwd() legt den aktuellen Arbeitsordner als Basis fest. Die Starter-Batchdatei wechselt vorher mit cd /d "%~dp0" in ihren eigenen Ordner. Dadurch ist der Arbeitsordner genau der Ordner, in dem beide Kursdateien liegen.
basis_ordner.iterdir() liefert die direkt enthaltenen Elemente. Mit is_file() werden daraus ausschließlich Dateien in die Liste dateien aufgenommen.
Für jede Datei werden zuerst die Dateiendung und ein möglicherweise vorhandener Zeitstempel geprüft. Nur wenn alle Prüfungen erfolgreich sind, wird ein Dictionary an plaene angehängt. datei.rename() kommt erst später im Echtmodus zum Einsatz.
Das reguläre Ausdrucksmuster verhindert, dass ein zweiter Zeitstempel vor einen bereits gestempelten Dateinamen gesetzt wird.
Starter-Batchdatei
@echo off
REM ============================================================
REM TOPTORIALS OFFICE - Python Grundkurs
REM Script: 007_zeitstempel_im_dateinamen_starter_batch_test.bat
REM Beschreibung: Startet die Variante zum Hinzufuegen eines Zeitstempels.
REM Autor: Chris
REM YouTube: https://www.youtube.com/@TOPTORIALS-OFFICE
REM Webseite: https://www.toptorials.com
REM Version: 1.0
REM Stand: 2026-08-26
REM
REM Hinweis:
REM Solange _test im Dateinamen steht, wird --test uebergeben.
REM Fuer die echte Ausfuehrung _test aus dem Dateinamen entfernen.
REM ============================================================
chcp 65001 >nul
cd /d "%~dp0"
echo %~n0 | find /i "_test" >nul
if not errorlevel 1 (
py "007_zeitstempel_im_dateinamen.py" --test
REM Alternative, falls py nicht funktioniert:
REM python "007_zeitstempel_im_dateinamen.py" --test
) else (
py "007_zeitstempel_im_dateinamen.py"
REM Alternative, falls py nicht funktioniert:
REM python "007_zeitstempel_im_dateinamen.py"
)
chcp 65001 aktiviert die UTF-8-Codepage für Umlaute. %~dp0 liefert den Ordner der Starter-Batchdatei. %~n0 liefert ihren Dateinamen ohne Erweiterung, damit find nach _test suchen kann.
Variante 2: Zeitstempel im aktuellen Ordner entfernen
So wird die Variante angewendet
Kopiere das Python-Skript und die Starter-Batchdatei aus 02_Zeitstempel_aus_Dateinamen_entfernen in den Ordner mit den zuvor umbenannten Dateien.
2026-08-26_14-35-42_Rechnung.pdf
2026-08-26_14-35-42_Bericht.docx
2026-08-26_14-35-42_Foto.jpg
Im Testmodus zeigt das Skript zunächst die geplanten ursprünglichen Namen an. Im bestätigten Echtmodus wird der erkannte Zeitstempel am Anfang entfernt.
Rechnung.pdf
Bericht.docx
Foto.jpg
Vollständiges Python-Skript
# ============================================================
# TOPTORIALS OFFICE - Python Grundkurs
# Script: 007_zeitstempel_aus_dateinamen_entfernen.py
# Beschreibung: Entfernt Zeitstempel aus Dateinamen im aktuellen Ordner.
# Autor: Chris
# YouTube: https://www.youtube.com/@TOPTORIALS-OFFICE
# Webseite: https://www.toptorials.com
# Version: 1.0
# Stand: 2026-08-26
# ============================================================
from datetime import datetime
from pathlib import Path
import re
import subprocess
import sys
print()
basis_ordner = Path.cwd()
testmodus = "--test" in sys.argv
zeitstempel_muster = re.compile(
r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}_?"
)
ausgeschlossene_endungen = (".bat", ".cmd", ".ps1", ".py", ".log")
# ------------------------------------------------------------
# Welche zusätzliche Ausgabe soll erstellt werden?
# Wichtig: Immer nur EINE der folgenden Varianten aktiv lassen.
# ------------------------------------------------------------
# Variante 1: Nur Bildschirmausgabe
logdatei_erstellen = False
zwischenablage_verwenden = False
# Variante 2: Logdatei erstellen
# logdatei_erstellen = True
# zwischenablage_verwenden = False
# Variante 3: Nur in die Zwischenablage kopieren
# logdatei_erstellen = False
# zwischenablage_verwenden = True
# Variante 4: Logdatei erstellen und in die Zwischenablage kopieren
# logdatei_erstellen = True
# zwischenablage_verwenden = True
# ------------------------------------------------------------
# Freien Zielnamen ermitteln
# Bereits vorhandene und bereits geplante Ziele berücksichtigen.
# ------------------------------------------------------------
def get_freier_zielname(ordner, basis_name, erweiterung, reservierte_ziele):
ziel_name = basis_name + erweiterung
ziel_pfad = ordner / ziel_name
vergleichs_pfad = str(ziel_pfad.absolute()).casefold()
if not ziel_pfad.exists() and vergleichs_pfad not in reservierte_ziele:
reservierte_ziele.add(vergleichs_pfad)
return ziel_name, ziel_pfad
nummer = 1
while True:
nummer_text = f"{nummer:03d}"
ziel_name = basis_name + "_" + nummer_text + erweiterung
ziel_pfad = ordner / ziel_name
vergleichs_pfad = str(ziel_pfad.absolute()).casefold()
if not ziel_pfad.exists() and vergleichs_pfad not in reservierte_ziele:
reservierte_ziele.add(vergleichs_pfad)
return ziel_name, ziel_pfad
nummer += 1
# ------------------------------------------------------------
# Modus, Zähler, Protokoll und Pläne vorbereiten
# ------------------------------------------------------------
if testmodus:
modus_text = "TESTMODUS"
else:
modus_text = "ECHTMODUS"
anzahl_umbenannt = 0
anzahl_uebersprungen = 0
anzahl_fehler = 0
protokoll = []
plaene = []
reservierte_ziele = set()
protokoll.append(
"Zeitstempel werden aus Dateinamen im aktuellen Ordner entfernt."
)
protokoll.append(f"Modus: {modus_text}")
protokoll.append(f"Ordner: {basis_ordner}")
protokoll.append("Erkanntes Format: JJJJ-MM-TT_HH-MM-SS_")
protokoll.append(
"Ausgeschlossene Endungen: " + ", ".join(ausgeschlossene_endungen)
)
protokoll.append("")
print("Zeitstempel werden aus Dateinamen im aktuellen Ordner entfernt...")
print()
print("Ordner:")
print(basis_ordner)
print()
print(f"Modus: {modus_text}")
print("Erkanntes Format: JJJJ-MM-TT_HH-MM-SS_")
print("Ausgeschlossene Endungen: " + ", ".join(ausgeschlossene_endungen))
print()
# ------------------------------------------------------------
# Direkt enthaltene Dateien erfassen
# Unterordner werden nicht durchsucht.
# ------------------------------------------------------------
dateien = []
try:
for pfad in basis_ordner.iterdir():
if pfad.is_file():
dateien.append(pfad)
except OSError as fehler:
print(
"FEHLER: Der Ordnerinhalt konnte nicht gelesen werden. "
f"Grund:\n{fehler}"
)
print()
input("Drücke Enter zum Beenden")
sys.exit()
# ------------------------------------------------------------
# Dateien prüfen und Umbenennungen planen
# ------------------------------------------------------------
for datei in dateien:
if datei.suffix.casefold() in ausgeschlossene_endungen:
meldung = (
"ÜBERSPRUNGEN:\n"
f'"{datei}"\n'
"- Dateiendung ist ausgeschlossen\n"
"---"
)
print(meldung)
protokoll.append(meldung)
anzahl_uebersprungen += 1
continue
if not zeitstempel_muster.match(datei.name):
meldung = (
"ÜBERSPRUNGEN:\n"
f'"{datei}"\n'
"- kein Zeitstempel am Anfang gefunden\n"
"---"
)
print(meldung)
protokoll.append(meldung)
anzahl_uebersprungen += 1
continue
basis_name = zeitstempel_muster.sub("", datei.stem, count=1)
if not basis_name:
basis_name = "Unbenannt"
neuer_name, ziel_pfad = get_freier_zielname(
basis_ordner,
basis_name,
datei.suffix,
reservierte_ziele,
)
plaene.append(
{
"quelle": datei,
"alter_name": datei.name,
"neuer_name": neuer_name,
"ziel": ziel_pfad,
}
)
# ------------------------------------------------------------
# Sicherheitsabfrage bei der echten Ausführung
# ------------------------------------------------------------
if not testmodus and plaene:
print()
print("ACHTUNG: Dieses Skript wird Dateien wirklich umbenennen.")
print()
antwort = input("Zum Fortfahren JA eingeben: ")
if antwort.upper() != "JA":
print()
print("Abgebrochen.")
print()
input("Drücke Enter zum Beenden")
sys.exit()
print()
# ------------------------------------------------------------
# Geplante Umbenennungen ausführen
# ------------------------------------------------------------
for plan in plaene:
if testmodus:
meldung = (
"WÜRDE UMBENENNEN:\n"
f'Quelle: "{plan["quelle"]}"\n'
f'Ziel: "{plan["ziel"]}"\n'
"---"
)
print(meldung)
protokoll.append(meldung)
anzahl_umbenannt += 1
continue
try:
plan["quelle"].rename(plan["ziel"])
meldung = (
"UMBENANNT:\n"
f'Quelle: "{plan["quelle"]}"\n'
f'Ziel: "{plan["ziel"]}"\n'
"---"
)
print(meldung)
protokoll.append(meldung)
anzahl_umbenannt += 1
except OSError as fehler:
meldung = (
"FEHLER:\n"
f'"{plan["quelle"]}"\n'
"konnte nicht umbenannt werden. Grund:\n"
f"{fehler}"
)
print(meldung)
protokoll.append(meldung)
anzahl_fehler += 1
# ------------------------------------------------------------
# Zusammenfassung
# ------------------------------------------------------------
protokoll.append("")
protokoll.append("Fertig.")
print()
print("Fertig.")
print()
if testmodus:
meldung_1 = f"Anzahl Dateien, die umbenannt würden: {anzahl_umbenannt}"
meldung_2 = f"Anzahl übersprungen: {anzahl_uebersprungen}"
meldung_3 = f"Anzahl Fehler: {anzahl_fehler}"
print(meldung_1)
print(meldung_2)
print(meldung_3)
print()
print("Es wurden keine Dateien umbenannt.")
print(
"Wenn alles richtig aussieht, entferne _test aus dem Namen "
"der Starter-Batchdatei."
)
protokoll.append(meldung_1)
protokoll.append(meldung_2)
protokoll.append(meldung_3)
protokoll.append("Es wurden keine Dateien umbenannt.")
else:
meldung_1 = f"Anzahl umbenannt: {anzahl_umbenannt}"
meldung_2 = f"Anzahl übersprungen: {anzahl_uebersprungen}"
meldung_3 = f"Anzahl Fehler: {anzahl_fehler}"
print(meldung_1)
print(meldung_2)
print(meldung_3)
protokoll.append(meldung_1)
protokoll.append(meldung_2)
protokoll.append(meldung_3)
# ------------------------------------------------------------
# Optionale Ausgabe als Logdatei und in die Zwischenablage
# ------------------------------------------------------------
protokoll_text = "\n".join(protokoll)
protokoll_zeitstempel = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
logdatei = (
basis_ordner
/ f"umbenennprotokoll_zeitstempel_entfernen_{protokoll_zeitstempel}.log"
)
if logdatei_erstellen:
try:
logdatei.write_text(protokoll_text, encoding="utf-8")
print()
print("Das Protokoll wurde erstellt:")
print(logdatei)
except OSError as fehler:
print()
print(
"FEHLER: Das Protokoll konnte nicht erstellt werden. "
f"Grund: {fehler}"
)
if zwischenablage_verwenden:
try:
subprocess.run(
"clip",
input=protokoll_text,
text=True,
encoding="utf-8",
check=True,
)
print()
print("Das Protokoll wurde zusätzlich in die Zwischenablage kopiert.")
except (OSError, subprocess.CalledProcessError) as fehler:
print()
print(
"FEHLER: Das Protokoll konnte nicht kopiert werden. "
f"Grund: {fehler}"
)
print()
input("Drücke Enter zum Beenden")
So funktioniert die Entfernen-Variante
zeitstempel_muster.match(datei.name) prüft, ob der Basisname tatsächlich mit einem passenden Zeitstempel beginnt. Ohne Treffer wird die Datei übersprungen.
Mit zeitstempel_muster.sub("", datei.stem, count=1) wird genau der erste erkannte Zeitstempel aus dem Basisnamen entfernt. Die Dateiendung aus datei.suffix bleibt separat erhalten.
Die Funktion get_freier_zielname() schützt vorhandene Dateien. Ist beispielsweise Rechnung.pdf schon vorhanden, entstehen automatisch Rechnung_001.pdf, Rechnung_002.pdf und weitere freie Namen.
Das Set reservierte_ziele berücksichtigt zusätzlich Zielpfade, die im aktuellen Durchlauf bereits geplant, aber noch nicht im Dateisystem vorhanden sind. Die Pfade werden mit casefold() für einen Vergleich ohne Beachtung der Groß- und Kleinschreibung vorbereitet.
Starter-Batchdatei
@echo off
REM ============================================================
REM TOPTORIALS OFFICE - Python Grundkurs
REM Script: 007_zeitstempel_aus_dateinamen_entfernen_starter_batch_test.bat
REM Beschreibung: Startet die Variante zum Entfernen eines Zeitstempels.
REM Autor: Chris
REM YouTube: https://www.youtube.com/@TOPTORIALS-OFFICE
REM Webseite: https://www.toptorials.com
REM Version: 1.0
REM Stand: 2026-08-26
REM
REM Hinweis:
REM Solange _test im Dateinamen steht, wird --test uebergeben.
REM Fuer die echte Ausfuehrung _test aus dem Dateinamen entfernen.
REM ============================================================
chcp 65001 >nul
cd /d "%~dp0"
echo %~n0 | find /i "_test" >nul
if not errorlevel 1 (
py "007_zeitstempel_aus_dateinamen_entfernen.py" --test
REM Alternative, falls py nicht funktioniert:
REM python "007_zeitstempel_aus_dateinamen_entfernen.py" --test
) else (
py "007_zeitstempel_aus_dateinamen_entfernen.py"
REM Alternative, falls py nicht funktioniert:
REM python "007_zeitstempel_aus_dateinamen_entfernen.py"
)
chcp 65001 aktiviert die UTF-8-Codepage für Umlaute. %~dp0 liefert den Ordner der Starter-Batchdatei. %~n0 liefert ihren Dateinamen ohne Erweiterung, damit find nach _test suchen kann.
Variante 3: Zeitstempel über „Senden an“ erstellen
SendTo-Verknüpfung einrichten
Für die SendTo-Variante wird eine Verknüpfung zur Starter-Batchdatei erstellt. Diese Verknüpfung kommt in den persönlichen Windows-Ordner shell:sendto.
Windows-Taste + Rdrücken.shell:sendtoeingeben und bestätigen.- Eine Verknüpfung zur Starter-Batchdatei in diesen Ordner kopieren.
So wird die Variante angewendet
Markiere im Windows-Explorer eine oder mehrere Dateien beziehungsweise Ordner. Öffne mit der rechten Maustaste das Kontextmenü und wähle anschließend Senden an und die angelegte Verknüpfung.
Bei übergebenen Ordnern verarbeitet das Skript nur die direkt enthaltenen Dateien. Übergebene Einzeldateien werden direkt aufgenommen. Tiefer liegende Unterordner werden nicht durchsucht.
Vollständiges Python-Skript
# ============================================================
# TOPTORIALS OFFICE - Python Grundkurs
# Script: 007_sendto_zeitstempel_im_dateinamen.py
# Beschreibung: Fügt übergebenen Dateien und Dateien in übergebenen Ordnern einen Zeitstempel hinzu.
# Autor: Chris
# YouTube: https://www.youtube.com/@TOPTORIALS-OFFICE
# Webseite: https://www.toptorials.com
# Version: 1.0
# Stand: 2026-08-26
# ============================================================
from datetime import datetime
from pathlib import Path
import re
import subprocess
import sys
print()
argumente = sys.argv[1:]
testmodus = "--test" in argumente
# ------------------------------------------------------------
# Das Argument "--test" aus den übrigen Argumenten herausfiltern
# ------------------------------------------------------------
argumente_ohne_test = []
for argument in argumente:
if argument != "--test":
argumente_ohne_test.append(argument)
argumente = argumente_ohne_test
# ------------------------------------------------------------
# Prüfen, ob mindestens ein Element übergeben wurde
# ------------------------------------------------------------
if not argumente:
print("Bitte sende eine oder mehrere Dateien oder Ordner an dieses Skript.")
print()
print("Du kannst im Explorer Dateien oder Ordner markieren,")
print("rechts anklicken und anschließend 'Senden an' verwenden.")
print()
input("Drücke Enter zum Beenden")
sys.exit()
zeitstempel = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
zeitstempel_muster = re.compile(
r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}_"
)
ausgeschlossene_endungen = (".bat", ".cmd", ".ps1", ".py", ".log")
# ------------------------------------------------------------
# Welche zusätzliche Ausgabe soll erstellt werden?
# Wichtig: Immer nur EINE der folgenden Varianten aktiv lassen.
# ------------------------------------------------------------
# Variante 1: Nur Bildschirmausgabe
logdatei_erstellen = False
zwischenablage_verwenden = False
# Variante 2: Logdatei erstellen
# logdatei_erstellen = True
# zwischenablage_verwenden = False
# Variante 3: Nur in die Zwischenablage kopieren
# logdatei_erstellen = False
# zwischenablage_verwenden = True
# Variante 4: Logdatei erstellen und in die Zwischenablage kopieren
# logdatei_erstellen = True
# zwischenablage_verwenden = True
# ------------------------------------------------------------
# Modus, Zähler, Protokoll und Pläne vorbereiten
# ------------------------------------------------------------
if testmodus:
modus_text = "TESTMODUS"
else:
modus_text = "ECHTMODUS"
anzahl_umbenannt = 0
anzahl_uebersprungen = 0
anzahl_fehler = 0
anzahl_ordner = 0
anzahl_einzeldateien = 0
protokoll = []
plaene = []
dateien = []
bereits_erfasste_dateien = set()
protokoll.append(
"Übergebene Dateien und Dateien in übergebenen Ordnern "
"erhalten einen Zeitstempel."
)
protokoll.append(f"Modus: {modus_text}")
protokoll.append(f"Zeitstempel: {zeitstempel}")
protokoll.append(
"Ausgeschlossene Endungen: " + ", ".join(ausgeschlossene_endungen)
)
protokoll.append("")
print(
"Übergebene Dateien und Dateien in übergebenen Ordnern "
"erhalten einen Zeitstempel..."
)
print()
print(f"Zeitstempel: {zeitstempel}")
print(f"Modus: {modus_text}")
print("Ausgeschlossene Endungen: " + ", ".join(ausgeschlossene_endungen))
print()
# ------------------------------------------------------------
# Doppelt übergebene Pfade entfernen
# ------------------------------------------------------------
eindeutige_argumente = []
bereits_vorhanden = set()
for element_pfad in argumente:
vergleichs_pfad = str(Path(element_pfad).absolute()).casefold()
if vergleichs_pfad not in bereits_vorhanden:
bereits_vorhanden.add(vergleichs_pfad)
eindeutige_argumente.append(element_pfad)
# ------------------------------------------------------------
# Übergebene Dateien und Ordner erfassen
# Ordnerinhalte werden nicht rekursiv durchsucht.
# ------------------------------------------------------------
for element_pfad in eindeutige_argumente:
element = Path(element_pfad)
if not element.exists():
meldung = (
"ÜBERSPRUNGEN:\n"
f'"{element_pfad}"\n'
"- Datei oder Ordner wurde nicht gefunden\n"
"---"
)
print(meldung)
protokoll.append(meldung)
anzahl_uebersprungen += 1
continue
try:
element = element.resolve()
if element.is_dir():
if element.parent == element:
meldung = (
"ÜBERSPRUNGEN:\n"
f'"{element}"\n'
"- Die oberste Ebene eines Laufwerks wird aus "
"Sicherheitsgründen nicht verarbeitet\n"
"---"
)
print(meldung)
protokoll.append(meldung)
anzahl_uebersprungen += 1
continue
print("Ordner wird verarbeitet:")
print(element)
print()
protokoll.append(f"Verarbeiteter Ordner: {element}")
anzahl_ordner += 1
for pfad in element.iterdir():
if pfad.is_file():
vergleichs_pfad = str(pfad.absolute()).casefold()
if vergleichs_pfad not in bereits_erfasste_dateien:
bereits_erfasste_dateien.add(vergleichs_pfad)
dateien.append(pfad)
protokoll.append("")
continue
if element.is_file():
anzahl_einzeldateien += 1
vergleichs_pfad = str(element.absolute()).casefold()
if vergleichs_pfad not in bereits_erfasste_dateien:
bereits_erfasste_dateien.add(vergleichs_pfad)
dateien.append(element)
continue
meldung = (
"ÜBERSPRUNGEN:\n"
f'"{element}"\n'
"- Der übergebene Pfad ist keine Datei und kein Ordner\n"
"---"
)
print(meldung)
protokoll.append(meldung)
anzahl_uebersprungen += 1
except OSError as fehler:
meldung = (
"FEHLER:\n"
f'"{element_pfad}"\n'
"konnte nicht eingelesen werden. Grund:\n"
f"{fehler}"
)
print(meldung)
protokoll.append(meldung)
anzahl_fehler += 1
# ------------------------------------------------------------
# Dateien prüfen und Umbenennungen planen
# ------------------------------------------------------------
for datei in dateien:
if datei.suffix.casefold() in ausgeschlossene_endungen:
meldung = (
"ÜBERSPRUNGEN:\n"
f'"{datei}"\n'
"- Dateiendung ist ausgeschlossen\n"
"---"
)
print(meldung)
protokoll.append(meldung)
anzahl_uebersprungen += 1
continue
if zeitstempel_muster.match(datei.name):
meldung = (
"ÜBERSPRUNGEN:\n"
f'"{datei}"\n'
"- Zeitstempel ist bereits vorhanden\n"
"---"
)
print(meldung)
protokoll.append(meldung)
anzahl_uebersprungen += 1
continue
neuer_name = zeitstempel + "_" + datei.name
ziel_pfad = datei.parent / neuer_name
if ziel_pfad.exists():
meldung = (
"ÜBERSPRUNGEN:\n"
f'"{datei}"\n'
"- Zieldatei existiert bereits:\n"
f'"{ziel_pfad}"\n'
"---"
)
print(meldung)
protokoll.append(meldung)
anzahl_uebersprungen += 1
continue
plaene.append(
{
"quelle": datei,
"alter_name": datei.name,
"neuer_name": neuer_name,
"ziel": ziel_pfad,
}
)
# ------------------------------------------------------------
# Sicherheitsabfrage bei der echten Ausführung
# ------------------------------------------------------------
if not testmodus and plaene:
print()
print("ACHTUNG: Dieses Skript wird Dateien wirklich umbenennen.")
print()
antwort = input("Zum Fortfahren JA eingeben: ")
if antwort.upper() != "JA":
print()
print("Abgebrochen.")
print()
input("Drücke Enter zum Beenden")
sys.exit()
print()
# ------------------------------------------------------------
# Geplante Umbenennungen ausführen
# ------------------------------------------------------------
for plan in plaene:
if testmodus:
meldung = (
"WÜRDE UMBENENNEN:\n"
f'Quelle: "{plan["quelle"]}"\n'
f'Ziel: "{plan["ziel"]}"\n'
"---"
)
print(meldung)
protokoll.append(meldung)
anzahl_umbenannt += 1
continue
try:
plan["quelle"].rename(plan["ziel"])
meldung = (
"UMBENANNT:\n"
f'Quelle: "{plan["quelle"]}"\n'
f'Ziel: "{plan["ziel"]}"\n'
"---"
)
print(meldung)
protokoll.append(meldung)
anzahl_umbenannt += 1
except OSError as fehler:
meldung = (
"FEHLER:\n"
f'"{plan["quelle"]}"\n'
"konnte nicht umbenannt werden. Grund:\n"
f"{fehler}"
)
print(meldung)
protokoll.append(meldung)
anzahl_fehler += 1
# ------------------------------------------------------------
# Zusammenfassung
# ------------------------------------------------------------
protokoll.append("")
protokoll.append("Fertig.")
print()
print("Fertig.")
print()
meldung_dateien = f"Übergebene Einzeldateien: {anzahl_einzeldateien}"
meldung_ordner = f"Verarbeitete Ordner: {anzahl_ordner}"
print(meldung_dateien)
print(meldung_ordner)
protokoll.append(meldung_dateien)
protokoll.append(meldung_ordner)
if testmodus:
meldung_1 = f"Anzahl Dateien, die umbenannt würden: {anzahl_umbenannt}"
meldung_2 = f"Anzahl übersprungen: {anzahl_uebersprungen}"
meldung_3 = f"Anzahl Fehler: {anzahl_fehler}"
print(meldung_1)
print(meldung_2)
print(meldung_3)
print()
print("Es wurden keine Dateien umbenannt.")
print(
"Wenn alles richtig aussieht, entferne _test aus dem Namen "
"der Starter-Batchdatei."
)
protokoll.append(meldung_1)
protokoll.append(meldung_2)
protokoll.append(meldung_3)
protokoll.append("Es wurden keine Dateien umbenannt.")
else:
meldung_1 = f"Anzahl umbenannt: {anzahl_umbenannt}"
meldung_2 = f"Anzahl übersprungen: {anzahl_uebersprungen}"
meldung_3 = f"Anzahl Fehler: {anzahl_fehler}"
print(meldung_1)
print(meldung_2)
print(meldung_3)
protokoll.append(meldung_1)
protokoll.append(meldung_2)
protokoll.append(meldung_3)
# ------------------------------------------------------------
# Optionale Ausgabe als Logdatei und in die Zwischenablage
# ------------------------------------------------------------
protokoll_text = "\n".join(protokoll)
protokoll_zeitstempel = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
skript_ordner = Path(__file__).resolve().parent
logdatei = (
skript_ordner
/ f"umbenennprotokoll_sendto_zeitstempel_setzen_{protokoll_zeitstempel}.log"
)
if logdatei_erstellen:
try:
logdatei.write_text(protokoll_text, encoding="utf-8")
print()
print("Das Protokoll wurde erstellt:")
print(logdatei)
except OSError as fehler:
print()
print(
"FEHLER: Das Protokoll konnte nicht erstellt werden. "
f"Grund: {fehler}"
)
if zwischenablage_verwenden:
try:
subprocess.run(
"clip",
input=protokoll_text,
text=True,
encoding="utf-8",
check=True,
)
print()
print("Das Protokoll wurde zusätzlich in die Zwischenablage kopiert.")
except (OSError, subprocess.CalledProcessError) as fehler:
print()
print(
"FEHLER: Das Protokoll konnte nicht kopiert werden. "
f"Grund: {fehler}"
)
print()
input("Drücke Enter zum Beenden")
So funktioniert die SendTo-Erstellen-Variante
sys.argv[1:] übernimmt alle Argumente hinter dem Namen des Python-Skripts. Dazu gehören der Schalter --test und die vom Explorer übergebenen Pfade. Der Testschalter wird anschließend aus der eigentlichen Pfadliste herausgefiltert.
Die Sets bereits_vorhanden und bereits_erfasste_dateien verhindern doppelte Verarbeitung. Das ist wichtig, wenn dieselbe Datei direkt und zusätzlich über ihren Ordner in derselben SendTo-Auswahl auftaucht.
Path.resolve() löst einen übergebenen Pfad auf. is_dir() und is_file() unterscheiden Ordner und Dateien. Laufwerkswurzeln werden aus Sicherheitsgründen übersprungen.
Für alle erfassten Dateien wird derselbe einmalig erzeugte Zeitstempel verwendet.
Starter-Batchdatei
@echo off
REM ============================================================
REM TOPTORIALS OFFICE - Python Grundkurs
REM Script: 007_sendto_zeitstempel_im_dateinamen_starter_batch_test.bat
REM Beschreibung: Startet die SendTo-Variante zum Hinzufuegen eines Zeitstempels.
REM Autor: Chris
REM YouTube: https://www.youtube.com/@TOPTORIALS-OFFICE
REM Webseite: https://www.toptorials.com
REM Version: 1.0
REM Stand: 2026-08-26
REM
REM Hinweis:
REM Solange _test im Dateinamen steht, wird --test uebergeben.
REM Fuer die echte Ausfuehrung _test aus dem Dateinamen entfernen.
REM ============================================================
chcp 65001 >nul
cd /d "%~dp0"
echo %~n0 | find /i "_test" >nul
if not errorlevel 1 (
py "007_sendto_zeitstempel_im_dateinamen.py" --test %*
REM Alternative, falls py nicht funktioniert:
REM python "007_sendto_zeitstempel_im_dateinamen.py" --test %*
) else (
py "007_sendto_zeitstempel_im_dateinamen.py" %*
REM Alternative, falls py nicht funktioniert:
REM python "007_sendto_zeitstempel_im_dateinamen.py" %*
)
chcp 65001 aktiviert die UTF-8-Codepage für Umlaute. %~dp0 liefert den Ordner der Starter-Batchdatei. %~n0 liefert ihren Dateinamen ohne Erweiterung, damit find nach _test suchen kann.
Bei dieser SendTo-Starterdatei übergibt %* zusätzlich sämtliche Pfade aus der Explorer-Auswahl an Python.
Variante 4: Zeitstempel über „Senden an“ entfernen
SendTo-Verknüpfung einrichten
Auch für die vierte Variante wird eine eigene Verknüpfung zur zugehörigen Starter-Batchdatei im Ordner shell:sendto angelegt.
So wird die Variante angewendet
Markiere die gestempelten Dateien oder ihre Ordner im Windows-Explorer und sende sie an die Verknüpfung der Entfernen-Variante. Der Testmodus zeigt zuerst sämtliche geplanten Zielnamen an.
Beim Entfernen werden Namenskonflikte nicht einfach übersprungen. Stattdessen sucht das Skript automatisch den nächsten freien Zielnamen.
Vollständiges Python-Skript
# ============================================================
# TOPTORIALS OFFICE - Python Grundkurs
# Script: 007_sendto_zeitstempel_aus_dateinamen_entfernen.py
# Beschreibung: Entfernt Zeitstempel aus übergebenen Dateien und Dateien in übergebenen Ordnern.
# Autor: Chris
# YouTube: https://www.youtube.com/@TOPTORIALS-OFFICE
# Webseite: https://www.toptorials.com
# Version: 1.0
# Stand: 2026-08-26
# ============================================================
from datetime import datetime
from pathlib import Path
import re
import subprocess
import sys
print()
argumente = sys.argv[1:]
testmodus = "--test" in argumente
# ------------------------------------------------------------
# Das Argument "--test" aus den übrigen Argumenten herausfiltern
# ------------------------------------------------------------
argumente_ohne_test = []
for argument in argumente:
if argument != "--test":
argumente_ohne_test.append(argument)
argumente = argumente_ohne_test
# ------------------------------------------------------------
# Prüfen, ob mindestens ein Element übergeben wurde
# ------------------------------------------------------------
if not argumente:
print("Bitte sende eine oder mehrere Dateien oder Ordner an dieses Skript.")
print()
print("Du kannst im Explorer Dateien oder Ordner markieren,")
print("rechts anklicken und anschließend 'Senden an' verwenden.")
print()
input("Drücke Enter zum Beenden")
sys.exit()
zeitstempel_muster = re.compile(
r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}_?"
)
ausgeschlossene_endungen = (".bat", ".cmd", ".ps1", ".py", ".log")
# ------------------------------------------------------------
# Welche zusätzliche Ausgabe soll erstellt werden?
# Wichtig: Immer nur EINE der folgenden Varianten aktiv lassen.
# ------------------------------------------------------------
# Variante 1: Nur Bildschirmausgabe
logdatei_erstellen = False
zwischenablage_verwenden = False
# Variante 2: Logdatei erstellen
# logdatei_erstellen = True
# zwischenablage_verwenden = False
# Variante 3: Nur in die Zwischenablage kopieren
# logdatei_erstellen = False
# zwischenablage_verwenden = True
# Variante 4: Logdatei erstellen und in die Zwischenablage kopieren
# logdatei_erstellen = True
# zwischenablage_verwenden = True
# ------------------------------------------------------------
# Freien Zielnamen ermitteln
# Bereits vorhandene und bereits geplante Ziele berücksichtigen.
# ------------------------------------------------------------
def get_freier_zielname(ordner, basis_name, erweiterung, reservierte_ziele):
ziel_name = basis_name + erweiterung
ziel_pfad = ordner / ziel_name
vergleichs_pfad = str(ziel_pfad.absolute()).casefold()
if not ziel_pfad.exists() and vergleichs_pfad not in reservierte_ziele:
reservierte_ziele.add(vergleichs_pfad)
return ziel_name, ziel_pfad
nummer = 1
while True:
nummer_text = f"{nummer:03d}"
ziel_name = basis_name + "_" + nummer_text + erweiterung
ziel_pfad = ordner / ziel_name
vergleichs_pfad = str(ziel_pfad.absolute()).casefold()
if not ziel_pfad.exists() and vergleichs_pfad not in reservierte_ziele:
reservierte_ziele.add(vergleichs_pfad)
return ziel_name, ziel_pfad
nummer += 1
# ------------------------------------------------------------
# Modus, Zähler, Protokoll und Pläne vorbereiten
# ------------------------------------------------------------
if testmodus:
modus_text = "TESTMODUS"
else:
modus_text = "ECHTMODUS"
anzahl_umbenannt = 0
anzahl_uebersprungen = 0
anzahl_fehler = 0
anzahl_ordner = 0
anzahl_einzeldateien = 0
protokoll = []
plaene = []
dateien = []
bereits_erfasste_dateien = set()
reservierte_ziele = set()
protokoll.append(
"Zeitstempel werden aus übergebenen Dateien und Dateien in "
"übergebenen Ordnern entfernt."
)
protokoll.append(f"Modus: {modus_text}")
protokoll.append("Erkanntes Format: JJJJ-MM-TT_HH-MM-SS_")
protokoll.append(
"Ausgeschlossene Endungen: " + ", ".join(ausgeschlossene_endungen)
)
protokoll.append("")
print(
"Zeitstempel werden aus übergebenen Dateien und Dateien in "
"übergebenen Ordnern entfernt..."
)
print()
print(f"Modus: {modus_text}")
print("Erkanntes Format: JJJJ-MM-TT_HH-MM-SS_")
print("Ausgeschlossene Endungen: " + ", ".join(ausgeschlossene_endungen))
print()
# ------------------------------------------------------------
# Doppelt übergebene Pfade entfernen
# ------------------------------------------------------------
eindeutige_argumente = []
bereits_vorhanden = set()
for element_pfad in argumente:
vergleichs_pfad = str(Path(element_pfad).absolute()).casefold()
if vergleichs_pfad not in bereits_vorhanden:
bereits_vorhanden.add(vergleichs_pfad)
eindeutige_argumente.append(element_pfad)
# ------------------------------------------------------------
# Übergebene Dateien und Ordner erfassen
# Ordnerinhalte werden nicht rekursiv durchsucht.
# ------------------------------------------------------------
for element_pfad in eindeutige_argumente:
element = Path(element_pfad)
if not element.exists():
meldung = (
"ÜBERSPRUNGEN:\n"
f'"{element_pfad}"\n'
"- Datei oder Ordner wurde nicht gefunden\n"
"---"
)
print(meldung)
protokoll.append(meldung)
anzahl_uebersprungen += 1
continue
try:
element = element.resolve()
if element.is_dir():
if element.parent == element:
meldung = (
"ÜBERSPRUNGEN:\n"
f'"{element}"\n'
"- Die oberste Ebene eines Laufwerks wird aus "
"Sicherheitsgründen nicht verarbeitet\n"
"---"
)
print(meldung)
protokoll.append(meldung)
anzahl_uebersprungen += 1
continue
print("Ordner wird verarbeitet:")
print(element)
print()
protokoll.append(f"Verarbeiteter Ordner: {element}")
anzahl_ordner += 1
for pfad in element.iterdir():
if pfad.is_file():
vergleichs_pfad = str(pfad.absolute()).casefold()
if vergleichs_pfad not in bereits_erfasste_dateien:
bereits_erfasste_dateien.add(vergleichs_pfad)
dateien.append(pfad)
protokoll.append("")
continue
if element.is_file():
anzahl_einzeldateien += 1
vergleichs_pfad = str(element.absolute()).casefold()
if vergleichs_pfad not in bereits_erfasste_dateien:
bereits_erfasste_dateien.add(vergleichs_pfad)
dateien.append(element)
continue
meldung = (
"ÜBERSPRUNGEN:\n"
f'"{element}"\n'
"- Der übergebene Pfad ist keine Datei und kein Ordner\n"
"---"
)
print(meldung)
protokoll.append(meldung)
anzahl_uebersprungen += 1
except OSError as fehler:
meldung = (
"FEHLER:\n"
f'"{element_pfad}"\n'
"konnte nicht eingelesen werden. Grund:\n"
f"{fehler}"
)
print(meldung)
protokoll.append(meldung)
anzahl_fehler += 1
# ------------------------------------------------------------
# Dateien prüfen und Umbenennungen planen
# ------------------------------------------------------------
for datei in dateien:
if datei.suffix.casefold() in ausgeschlossene_endungen:
meldung = (
"ÜBERSPRUNGEN:\n"
f'"{datei}"\n'
"- Dateiendung ist ausgeschlossen\n"
"---"
)
print(meldung)
protokoll.append(meldung)
anzahl_uebersprungen += 1
continue
if not zeitstempel_muster.match(datei.name):
meldung = (
"ÜBERSPRUNGEN:\n"
f'"{datei}"\n'
"- kein Zeitstempel am Anfang gefunden\n"
"---"
)
print(meldung)
protokoll.append(meldung)
anzahl_uebersprungen += 1
continue
basis_name = zeitstempel_muster.sub("", datei.stem, count=1)
if not basis_name:
basis_name = "Unbenannt"
neuer_name, ziel_pfad = get_freier_zielname(
datei.parent,
basis_name,
datei.suffix,
reservierte_ziele,
)
plaene.append(
{
"quelle": datei,
"alter_name": datei.name,
"neuer_name": neuer_name,
"ziel": ziel_pfad,
}
)
# ------------------------------------------------------------
# Sicherheitsabfrage bei der echten Ausführung
# ------------------------------------------------------------
if not testmodus and plaene:
print()
print("ACHTUNG: Dieses Skript wird Dateien wirklich umbenennen.")
print()
antwort = input("Zum Fortfahren JA eingeben: ")
if antwort.upper() != "JA":
print()
print("Abgebrochen.")
print()
input("Drücke Enter zum Beenden")
sys.exit()
print()
# ------------------------------------------------------------
# Geplante Umbenennungen ausführen
# ------------------------------------------------------------
for plan in plaene:
if testmodus:
meldung = (
"WÜRDE UMBENENNEN:\n"
f'Quelle: "{plan["quelle"]}"\n'
f'Ziel: "{plan["ziel"]}"\n'
"---"
)
print(meldung)
protokoll.append(meldung)
anzahl_umbenannt += 1
continue
try:
plan["quelle"].rename(plan["ziel"])
meldung = (
"UMBENANNT:\n"
f'Quelle: "{plan["quelle"]}"\n'
f'Ziel: "{plan["ziel"]}"\n'
"---"
)
print(meldung)
protokoll.append(meldung)
anzahl_umbenannt += 1
except OSError as fehler:
meldung = (
"FEHLER:\n"
f'"{plan["quelle"]}"\n'
"konnte nicht umbenannt werden. Grund:\n"
f"{fehler}"
)
print(meldung)
protokoll.append(meldung)
anzahl_fehler += 1
# ------------------------------------------------------------
# Zusammenfassung
# ------------------------------------------------------------
protokoll.append("")
protokoll.append("Fertig.")
print()
print("Fertig.")
print()
meldung_dateien = f"Übergebene Einzeldateien: {anzahl_einzeldateien}"
meldung_ordner = f"Verarbeitete Ordner: {anzahl_ordner}"
print(meldung_dateien)
print(meldung_ordner)
protokoll.append(meldung_dateien)
protokoll.append(meldung_ordner)
if testmodus:
meldung_1 = f"Anzahl Dateien, die umbenannt würden: {anzahl_umbenannt}"
meldung_2 = f"Anzahl übersprungen: {anzahl_uebersprungen}"
meldung_3 = f"Anzahl Fehler: {anzahl_fehler}"
print(meldung_1)
print(meldung_2)
print(meldung_3)
print()
print("Es wurden keine Dateien umbenannt.")
print(
"Wenn alles richtig aussieht, entferne _test aus dem Namen "
"der Starter-Batchdatei."
)
protokoll.append(meldung_1)
protokoll.append(meldung_2)
protokoll.append(meldung_3)
protokoll.append("Es wurden keine Dateien umbenannt.")
else:
meldung_1 = f"Anzahl umbenannt: {anzahl_umbenannt}"
meldung_2 = f"Anzahl übersprungen: {anzahl_uebersprungen}"
meldung_3 = f"Anzahl Fehler: {anzahl_fehler}"
print(meldung_1)
print(meldung_2)
print(meldung_3)
protokoll.append(meldung_1)
protokoll.append(meldung_2)
protokoll.append(meldung_3)
# ------------------------------------------------------------
# Optionale Ausgabe als Logdatei und in die Zwischenablage
# ------------------------------------------------------------
protokoll_text = "\n".join(protokoll)
protokoll_zeitstempel = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
skript_ordner = Path(__file__).resolve().parent
logdatei = (
skript_ordner
/ f"umbenennprotokoll_sendto_zeitstempel_entfernen_{protokoll_zeitstempel}.log"
)
if logdatei_erstellen:
try:
logdatei.write_text(protokoll_text, encoding="utf-8")
print()
print("Das Protokoll wurde erstellt:")
print(logdatei)
except OSError as fehler:
print()
print(
"FEHLER: Das Protokoll konnte nicht erstellt werden. "
f"Grund: {fehler}"
)
if zwischenablage_verwenden:
try:
subprocess.run(
"clip",
input=protokoll_text,
text=True,
encoding="utf-8",
check=True,
)
print()
print("Das Protokoll wurde zusätzlich in die Zwischenablage kopiert.")
except (OSError, subprocess.CalledProcessError) as fehler:
print()
print(
"FEHLER: Das Protokoll konnte nicht kopiert werden. "
f"Grund: {fehler}"
)
print()
input("Drücke Enter zum Beenden")
So funktioniert die SendTo-Entfernen-Variante
Die Erfassung und Deduplizierung der SendTo-Argumente funktioniert wie in Variante 3. Danach prüft das Regex-Muster jeden Dateinamen am Anfang auf einen Zeitstempel.
get_freier_zielname() berücksichtigt sowohl tatsächlich vorhandene Dateien als auch die innerhalb desselben Durchlaufs bereits reservierten Ziele. So können auch umfangreiche SendTo-Auswahlen ohne gegenseitiges Überschreiben geplant werden.
Erst wenn alle gültigen Pläne feststehen, fragt das Skript im Echtmodus nach JA. Anschließend führt rename() die Umbenennungen einzeln innerhalb eines try-Blocks aus. Ein Fehler bei einer Datei beendet dadurch nicht automatisch den gesamten Durchlauf.
Starter-Batchdatei
@echo off
REM ============================================================
REM TOPTORIALS OFFICE - Python Grundkurs
REM Script: 007_sendto_zeitstempel_aus_dateinamen_entfernen_starter_batch_test.bat
REM Beschreibung: Startet die SendTo-Variante zum Entfernen eines Zeitstempels.
REM Autor: Chris
REM YouTube: https://www.youtube.com/@TOPTORIALS-OFFICE
REM Webseite: https://www.toptorials.com
REM Version: 1.0
REM Stand: 2026-08-26
REM
REM Hinweis:
REM Solange _test im Dateinamen steht, wird --test uebergeben.
REM Fuer die echte Ausfuehrung _test aus dem Dateinamen entfernen.
REM ============================================================
chcp 65001 >nul
cd /d "%~dp0"
echo %~n0 | find /i "_test" >nul
if not errorlevel 1 (
py "007_sendto_zeitstempel_aus_dateinamen_entfernen.py" --test %*
REM Alternative, falls py nicht funktioniert:
REM python "007_sendto_zeitstempel_aus_dateinamen_entfernen.py" --test %*
) else (
py "007_sendto_zeitstempel_aus_dateinamen_entfernen.py" %*
REM Alternative, falls py nicht funktioniert:
REM python "007_sendto_zeitstempel_aus_dateinamen_entfernen.py" %*
)
chcp 65001 aktiviert die UTF-8-Codepage für Umlaute. %~dp0 liefert den Ordner der Starter-Batchdatei. %~n0 liefert ihren Dateinamen ohne Erweiterung, damit find nach _test suchen kann.
Bei dieser SendTo-Starterdatei übergibt %* zusätzlich sämtliche Pfade aus der Explorer-Auswahl an Python.
Wo werden die Protokolle gespeichert?
Wenn logdatei_erstellen = True aktiviert ist, wird eine Textdatei mit Datum und Uhrzeit erzeugt. Bei den normalen Varianten liegt sie im Arbeitsordner. Die SendTo-Varianten speichern das Protokoll im Ordner des Python-Skripts.
Die Dateiendung .log ist von der Umbenennung ausgeschlossen. Dadurch benennt das Skript seine eigenen Protokolldateien nicht um.
Typische Meldungen
WÜRDE UMBENENNEN
Die Datei wurde erfolgreich geprüft. Im Testmodus werden Quelle und geplantes Ziel angezeigt, ohne die Datei zu verändern.
UMBENANNT
Die Umbenennung wurde im bestätigten Echtmodus erfolgreich ausgeführt.
Zeitstempel ist bereits vorhanden
Eine Erstellen-Variante hat am Anfang des Dateinamens bereits einen passenden Zeitstempel erkannt und überspringt die Datei.
Kein Zeitstempel am Anfang gefunden
Eine Entfernen-Variante hat am Anfang keinen passenden Zeitstempel gefunden. Ein ähnlicher Text an einer anderen Stelle im Dateinamen wird absichtlich nicht entfernt.
Dateiendung ist ausgeschlossen
Die Datei besitzt eine der Endungen .bat, .cmd, .ps1, .py oder .log.
Typische Fehler und mögliche Ursachen
Python oder der Python Launcher wurde nicht gefunden
Prüfe in einer neu geöffneten Eingabeaufforderung die Befehle python --version und py --version. Falls nur python funktioniert, kann in der Starter-Batchdatei die vorbereitete Alternative aktiviert werden.
Das Python-Skript wird nicht gefunden
Python-Skript und zugehörige Starter-Batchdatei müssen gemeinsam im gleichen Ordner liegen. Auch der in der Batchdatei eingetragene Python-Dateiname muss unverändert bleiben.
Es werden keine Dateien gefunden
Die Skripte arbeiten bewusst nicht rekursiv. Bei einem Ordner werden nur direkt enthaltene Dateien verarbeitet. Außerdem werden die festgelegten Kurs- und Protokolldateien ausgelassen.
Eine Datei wird übersprungen
Die Konsolenausgabe nennt den Grund. Möglich sind ein bereits vorhandener Zeitstempel, ein fehlender Zeitstempel, eine ausgeschlossene Dateiendung, ein ungültiger Pfad oder ein bereits vorhandener Zielname.
Eine Datei kann nicht umbenannt werden
Die Datei könnte in einem anderen Programm geöffnet sein, schreibgeschützt sein oder in einem Ordner liegen, für den keine ausreichenden Rechte vorhanden sind. Die genaue Meldung aus dem abgefangenen OSError wird ausgegeben.
Eine SendTo-Auswahl wird doppelt übergeben
Die Sets für bereits erfasste Pfade verhindern normalerweise eine doppelte Verarbeitung. Dabei wird mit casefold() auch eine abweichende Groß- und Kleinschreibung berücksichtigt.
Wichtige Python-Begriffe aus diesem Tutorial
datetime.now()
Liefert das aktuelle lokale Datum und die aktuelle lokale Uhrzeit als datetime-Objekt.
strftime()
Formatiert Datum und Uhrzeit anhand von Platzhaltern zu einem Text.
re.compile()
Erstellt aus einem Muster ein Regex-Objekt, das anschließend mehrfach mit match() oder sub() verwendet werden kann.
Path.cwd()
Liefert den aktuellen Arbeitsordner als Path-Objekt.
Path.iterdir()
Liefert die direkt enthaltenen Elemente eines Ordners. Die Methode durchsucht keine tieferen Ebenen.
Path.stem und Path.suffix
stem liefert den Dateinamen ohne die letzte Dateiendung. suffix liefert diese Dateiendung einschließlich Punkt.
casefold()
Bereitet Texte für einen Vergleich ohne Beachtung der Groß- und Kleinschreibung vor. Die tatsächliche Schreibweise der Datei wird dabei nicht verändert.
Liste mit Dictionaries
Die Liste plaene enthält für jede geplante Umbenennung ein Dictionary. Über Schlüssel wie "quelle" und "ziel" lassen sich die zugehörigen Werte eindeutig abrufen.
set()
Ein Set speichert eindeutige Werte. In den SendTo-Varianten verhindert es doppelte Pfade. In den Entfernen-Varianten reserviert es bereits geplante Zielnamen.
while True
Erzeugt eine Schleife, die so lange nach dem nächsten nummerierten Namen sucht, bis ein freier Zielpfad gefunden und mit return zurückgegeben wird.
try und except OSError
Fangen typische Fehler beim Lesen von Ordnern, Auflösen von Pfaden und Umbenennen von Dateien kontrolliert ab.
sys.argv
Enthält die beim Programmstart übergebenen Argumente. Dazu gehören in diesem Tutorial --test und bei SendTo die ausgewählten Datei- und Ordnerpfade.
Wichtige Batch-Begriffe aus diesem Tutorial
chcp 65001aktiviert die UTF-8-Codepage.%~dp0liefert Laufwerk und Ordner der Starter-Batchdatei.%~n0liefert ihren Dateinamen ohne Dateiendung.find /i "_test"sucht ohne Beachtung der Groß- und Kleinschreibung nach dem Testkennzeichen.%*übergibt bei SendTo sämtliche empfangenen Argumente weiter.pystartet Python über den Python Launcher.
Was haben wir in diesem Teil gelernt?
- Mit
datetime.now().strftime("%Y-%m-%d_%H-%M-%S")erzeugen wir einen einheitlichen Zeitstempel. - Alle Dateien eines Durchlaufs erhalten denselben Zeitstempel.
- Ein Regex-Muster erkennt Zeitstempel ausschließlich am Anfang des Dateinamens.
- Bereits gestempelte Dateien werden beim Hinzufügen übersprungen.
re.compile(),match()undsub()erkennen und entfernen Zeitstempel.get_freier_zielname()verhindert das Überschreiben vorhandener Dateien.- Bei Namenskonflikten werden
_001,_002und weitere Nummern ergänzt. reservierte_zieleverhindert doppelte Zielnamen innerhalb eines Durchlaufs.iterdir()ermittelt nur die direkt enthaltenen Elemente.- Ohne rekursiven Aufruf werden tiefere Unterordner nicht durchsucht.
.bat,.cmd,.ps1,.pyund.logwerden ausgeschlossen.- Listen mit Dictionaries speichern jede Umbenennung zuerst als Plan.
- Im Testmodus werden die Pläne nur angezeigt.
- Im Echtmodus werden sie nach der Bestätigung mit
JAausgeführt. - Die normalen Varianten arbeiten im Ordner der Starter-Batchdatei.
- Die SendTo-Varianten erhalten Dateien und Ordner aus dem Windows-Explorer.
sys.argvkann mehrere übergebene Pfade aufnehmen.%*reicht alle Pfade an Python weiter.bereits_erfasste_dateienverhindert eine doppelte Verarbeitung.
Damit besitzen wir für beide Arbeitsweisen einen vollständigen Ablauf. Wir können Zeitstempel hinzufügen, die geplanten Namen im Testmodus kontrollieren und den Zeitstempel später wieder sicher entfernen.
Video zu diesem Tutorial:
Das könnte Dich auch interessieren
Keine Einträge gefunden

