Toptorials Tutorials WeblogoLogobild kleinLogobild kleinToptorials Tutorials Weblogo
  • ALLE THEMEN IM ÜBERBLICK
  • YouTube
  • INFOS
    • SITEMAP
    • ÜBER TOPTORIALS
✕
            No results See all results

            PowerShell 7 Grundkurs Teil 007: Zeitstempel hinzufügen und entfernen

            • Startseite
            • Tutorials Windows Automatisierung Powershell 7 Grundkurs
            • PowerShell 7 Grundkurs Teil 007: Zeitstempel hinzufügen und entfernen

            PowerShell 7 Grundkurs Teil 007: Zeitstempel hinzufügen und entfernen

            Worum geht es in diesem Teil?

            In diesem Teil des PowerShell-7-Grundkurses versehen wir Dateinamen automatisch mit einem Zeitstempel und entfernen diesen bei Bedarf wieder.

            Aus einer Datei wie:

            Rechnung.pdf

            wird beispielsweise:

            2026-08-21_14-35-42_Rechnung.pdf
            Powershell 7 Dateiname mit Zeitstempel

            Powershell 7 Dateiname mit Zeitstempel

            Das verwendete Format lautet:

            JJJJ-MM-TT_HH-MM-SS_Dateiname.ext

            Wir erstellen vier aufeinander abgestimmte Varianten. Zuerst verwenden wir zwei Skripte, die gemeinsam mit ihren Starter-Batchdateien direkt in den zu bearbeitenden Ordner kopiert werden. Danach folgen zwei SendTo-Varianten für das Windows-Menü Senden an.

            1. Zeitstempel bei Dateien im aktuellen Ordner hinzufügen
            2. Zeitstempel bei Dateien im aktuellen Ordner wieder entfernen
            3. Zeitstempel über Senden an hinzufügen
            4. Zeitstempel über Senden an wieder entfernen

            Die Reihenfolge ist bewusst paarweise aufgebaut: Wir sehen zuerst die Anwendung zum Erstellen eines Zeitstempels und erklären danach den Code. Direkt anschließend folgt die passende Variante zum Entfernen. Danach wiederholen wir denselben Ablauf mit den beiden SendTo-Versionen.

            Was ist das Ziel?

            Mit einem Zeitstempel am Anfang lassen sich Dateien chronologisch sortieren und eindeutig einem bestimmten Bearbeitungszeitpunkt zuordnen. Das kann beispielsweise bei Exporten, Sicherungen, Berichten oder regelmäßig erzeugten Dateien hilfreich sein.

            Da ein solcher Präfix nicht immer dauerhaft benötigt wird, gehört zu jeder Erstellen-Variante eine passende Entfernen-Variante. Beide Richtungen unterstützen einen sicheren Testmodus, prüfen mögliche Namenskonflikte und führen die eigentliche Umbenennung erst nach allen Vorprüfungen aus.

            Warum ist dieser Teil wichtig?

            In diesem Tutorial verbinden wir mehrere wichtige Grundlagen aus PowerShell 7:

            • Get-Date -Format erzeugt einen einheitlich formatierten Zeitstempel.
            • Reguläre Ausdrücke erkennen einen Zeitstempel ausschließlich am Anfang eines Dateinamens.
            • Get-ChildItem -File ermittelt direkt enthaltene Dateien.
            • -contains prüft ausgeschlossene Dateiendungen.
            • Join-Path setzt Ordnerpfad und neuen Dateinamen sicher zusammen.
            • Test-Path schützt bereits vorhandene Zieldateien.
            • [PSCustomObject] speichert geplante Umbenennungen mit Quelle und Ziel.
            • HashSet[string] reserviert bereits geplante Zielpfade.
            • Rename-Item führt die Umbenennung im Echtmodus aus.
            • ValueFromRemainingArguments übernimmt bei SendTo mehrere übergebene Argumente.

            Hinweis zur Kursreihe

            Dieses Tutorial gehört zum PowerShell-7-Grundkurs. Der Aufbau orientiert sich an den entsprechenden Teilen für Batch und Python. Dadurch lassen sich die unterschiedlichen Schreibweisen und Möglichkeiten der drei Programmiersprachen besonders gut vergleichen.

            Downloads zu diesem Teil

            Dateien für PowerShell 7 Grundkurs Teil 007:

            Download PowerShell 7 Grundkurs: 007_Zeitstempel_im_Dateinamen als ZIP-Datei.

            Entpacken nach:

            C:\TOPTORIALS_CODE\010_PowerShell_7_Grundkurs

            Dadurch entsteht dieser Hauptordner:

            C:\TOPTORIALS_CODE\010_PowerShell_7_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 PowerShell 7 befindet sich der passende Testbereich anschließend unter:

            C:\TOPTORIALS_CODE\1000_TESTBEREICH\TOPTORIALS_Testdaten_Windows_Automatisierung_PowerShell_7_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 das jeweilige Skript umbenennen würde.

            Für den Echtmodus wird _test aus dem Namen der 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 PowerShell 7 mit dem Befehl pwsh
            • Schreibrechte in den zu bearbeitenden Ordnern
            • PowerShell-Skript und passende Starter-Batchdatei bleiben jeweils zusammen

            Enthaltene Ordner und Dateien

            007_Zeitstempel_im_Dateinamen
            │
            ├── 01_Zeitstempel_im_Dateinamen
            │   ├── 007_zeitstempel_im_dateinamen.ps1
            │   └── 007_zeitstempel_im_dateinamen_starter_batch_test.bat
            │
            ├── 02_Zeitstempel_aus_Dateinamen_entfernen
            │   ├── 007_zeitstempel_aus_dateinamen_entfernen.ps1
            │   └── 007_zeitstempel_aus_dateinamen_entfernen_starter_batch_test.bat
            │
            ├── 03_SendTo_Zeitstempel_im_Dateinamen
            │   ├── 007_sendto_zeitstempel_im_dateinamen.ps1
            │   └── 007_sendto_zeitstempel_im_dateinamen_starter_batch_test.bat
            │
            └── 04_SendTo_Zeitstempel_aus_Dateinamen_entfernen
                ├── 007_sendto_zeitstempel_aus_dateinamen_entfernen.ps1
                └── 007_sendto_zeitstempel_aus_dateinamen_entfernen_starter_batch_test.bat

            Das Zeitstempelformat

            Der Zeitstempel wird mit Get-Date genau einmal pro Skriptdurchlauf erzeugt:

            $zeitstempel = Get-Date -Format "yyyy-MM-dd_HH-mm-ss"

            Alle Dateien eines Durchlaufs erhalten damit denselben Zeitstempel. Die Sortierreihenfolge Jahr, Monat, Tag, Stunde, Minute und Sekunde sorgt dafür, dass die Dateinamen auch alphabetisch chronologisch angeordnet werden.

            Das abschließende Trennzeichen wird beim Zusammensetzen ergänzt:

            $neuerName = $zeitstempel + "_" + $datei.Name

            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 die Dateien verarbeitet, die direkt in diesem Ordner liegen. Inhalte weiterer Unterordner bleiben unverändert.

            Testmodus und Echtmodus

            Jede Starter-Batchdatei prüft ihren eigenen Dateinamen. Solange darin _test vorkommt, übergibt sie den Schalter -Test an das PowerShell-Skript.

            • Mit _test: Die geplanten Änderungen werden nur angezeigt.
            • Ohne _test: Nach der Bestätigung mit JA werden die Dateien wirklich umbenannt.

            Alle gültigen Umbenennungen werden zunächst in $plaene gesammelt. Erst eine zweite Schleife zeigt diese Pläne an oder führt sie aus.

            Ausgabe, Logdatei und Zwischenablage

            In allen vier PowerShell-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:

            $logDateiErstellen = $false
            $zwischenablageVerwenden = $false

            Die Ausgabe einer geplanten oder ausgeführten Umbenennung verteilt Quelle und Ziel auf eigene Zeilen. Dadurch bleiben auch lange Pfade gut lesbar.

            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. PowerShell-Skript und Starter-Batchdatei müssen gemeinsam dort liegen.

            Mein_Arbeitsordner
            ├── 007_zeitstempel_im_dateinamen.ps1
            ├── 007_zeitstempel_im_dateinamen_starter_batch_test.bat
            ├── Rechnung.pdf
            ├── Bericht.docx
            └── Foto.jpg

            Starte zuerst die Datei:

            007_zeitstempel_im_dateinamen_starter_batch_test.bat

            Im Testmodus werden nur die geplanten Zielnamen angezeigt. Wenn die Ausgabe stimmt, entfernst du _test aus dem Dateinamen der Starter-Batchdatei und startest sie erneut. Bestätige den Echtlauf anschließend mit JA.

            Danach können die Dateien beispielsweise so aussehen:

            2026-08-21_14-35-42_Rechnung.pdf
            2026-08-21_14-35-42_Bericht.docx
            2026-08-21_14-35-42_Foto.jpg

            Vollständiges PowerShell-7-Skript

            # ============================================================
            # TOPTORIALS OFFICE - PowerShell 7 Grundkurs
            # Script: 007_zeitstempel_im_dateinamen.ps1
            # 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-21
            # ============================================================
            
            param (
                [switch]$Test
            )
            
            Write-Host ""
            
            $basisOrdner = (Get-Location).Path
            $zeitstempel = Get-Date -Format "yyyy-MM-dd_HH-mm-ss"
            $zeitstempelMuster = '^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}_'
            $ausgeschlosseneEndungen = @(".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
            $logDateiErstellen = $false
            $zwischenablageVerwenden = $false
            
            # Variante 2: Logdatei erstellen
            # $logDateiErstellen = $true
            # $zwischenablageVerwenden = $false
            
            # Variante 3: Nur in die Zwischenablage kopieren
            # $logDateiErstellen = $false
            # $zwischenablageVerwenden = $true
            
            # Variante 4: Logdatei erstellen und in die Zwischenablage kopieren
            # $logDateiErstellen = $true
            # $zwischenablageVerwenden = $true
            
            
            # ------------------------------------------------------------
            # Modus, Zähler, Protokoll und Pläne vorbereiten
            # ------------------------------------------------------------
            
            if ($Test) {
                $modusText = "TESTMODUS"
            }
            else {
                $modusText = "ECHTMODUS"
            }
            
            $anzahlUmbenannt = 0
            $anzahlUebersprungen = 0
            $anzahlFehler = 0
            $protokoll = @()
            $plaene = @()
            
            $protokoll += "Dateien im aktuellen Ordner werden mit einem Zeitstempel umbenannt."
            $protokoll += "Modus: $modusText"
            $protokoll += "Ordner: $basisOrdner"
            $protokoll += "Zeitstempel: $zeitstempel"
            $protokoll += "Ausgeschlossene Endungen: $($ausgeschlosseneEndungen -join ', ')"
            $protokoll += ""
            
            Write-Host "Dateien im aktuellen Ordner werden mit einem Zeitstempel umbenannt..."
            Write-Host ""
            Write-Host "Ordner:"
            Write-Host $basisOrdner
            Write-Host ""
            Write-Host "Zeitstempel: $zeitstempel"
            Write-Host "Modus: $modusText"
            Write-Host "Ausgeschlossene Endungen: $($ausgeschlosseneEndungen -join ', ')"
            Write-Host ""
            
            
            # ------------------------------------------------------------
            # Direkt enthaltene Dateien erfassen
            # Unterordner werden nicht durchsucht.
            # ------------------------------------------------------------
            
            try {
                $dateien = @(Get-ChildItem -LiteralPath $basisOrdner -File -Force -ErrorAction Stop)
            }
            catch {
                Write-Host "FEHLER: Der Ordnerinhalt konnte nicht gelesen werden. Grund: $($_.Exception.Message)"
                Write-Host ""
                Read-Host "Drücke Enter zum Beenden"
                exit
            }
            
            
            # ------------------------------------------------------------
            # Dateien prüfen und Umbenennungen planen
            # ------------------------------------------------------------
            
            foreach ($datei in $dateien) {
                if ($ausgeschlosseneEndungen -contains $datei.Extension) {
                    $meldung = "ÜBERSPRUNGEN: `"$($datei.FullName)`" - Dateiendung ist ausgeschlossen"
                    Write-Host $meldung
                    $protokoll += $meldung
                    $anzahlUebersprungen++
                    continue
                }
            
                if ($datei.Name -match $zeitstempelMuster) {
                    $meldung = "ÜBERSPRUNGEN: `"$($datei.FullName)`" - Zeitstempel ist bereits vorhanden"
                    Write-Host $meldung
                    $protokoll += $meldung
                    $anzahlUebersprungen++
                    continue
                }
            
                $neuerName = $zeitstempel + "_" + $datei.Name
                $zielPfad = Join-Path -Path $basisOrdner -ChildPath $neuerName
            
                if (Test-Path -LiteralPath $zielPfad) {
                    $meldung = "ÜBERSPRUNGEN: `"$($datei.FullName)`" - Zieldatei existiert bereits: `"$zielPfad`""
                    Write-Host $meldung
                    $protokoll += $meldung
                    $anzahlUebersprungen++
                    continue
                }
            
                $plaene += [PSCustomObject]@{
                    Quelle    = $datei.FullName
                    AlterName = $datei.Name
                    NeuerName = $neuerName
                    Ziel      = $zielPfad
                }
            }
            
            
            # ------------------------------------------------------------
            # Sicherheitsabfrage bei der echten Ausführung
            # ------------------------------------------------------------
            
            if (-not $Test -and $plaene.Count -gt 0) {
                Write-Host ""
                Write-Host "ACHTUNG: Dieses Skript wird Dateien wirklich umbenennen."
                Write-Host ""
            
                $antwort = Read-Host "Zum Fortfahren JA eingeben"
            
                if ($antwort -ne "JA") {
                    Write-Host ""
                    Write-Host "Abgebrochen."
                    Write-Host ""
                    Read-Host "Drücke Enter zum Beenden"
                    exit
                }
            
                Write-Host ""
            }
            
            
            # ------------------------------------------------------------
            # Geplante Umbenennungen ausführen
            # ------------------------------------------------------------
            
            foreach ($plan in $plaene) {
                if ($Test) {
                    $meldung = "WÜRDE UMBENENNEN:`nQuelle: `"$($plan.Quelle)`"`nZiel:   `"$($plan.Ziel)`"`n---"
                    Write-Host $meldung
                    $protokoll += $meldung
                    $anzahlUmbenannt++
                    continue
                }
            
                try {
                    Rename-Item -LiteralPath $plan.Quelle -NewName $plan.NeuerName -ErrorAction Stop
            
                    $meldung = "UMBENANNT:`nQuelle: `"$($plan.Quelle)`"`nZiel:   `"$($plan.Ziel)`"`n---"
                    Write-Host $meldung
                    $protokoll += $meldung
                    $anzahlUmbenannt++
                }
                catch {
                    $meldung = "FEHLER: `"$($plan.Quelle)`" konnte nicht umbenannt werden. Grund: $($_.Exception.Message)"
                    Write-Host $meldung
                    $protokoll += $meldung
                    $anzahlFehler++
                }
            }
            
            
            # ------------------------------------------------------------
            # Zusammenfassung
            # ------------------------------------------------------------
            
            $protokoll += ""
            $protokoll += "Fertig."
            
            Write-Host ""
            Write-Host "Fertig."
            Write-Host ""
            
            if ($Test) {
                $meldung1 = "Anzahl Dateien, die umbenannt würden: $anzahlUmbenannt"
                $meldung2 = "Anzahl übersprungen: $anzahlUebersprungen"
                $meldung3 = "Anzahl Fehler: $anzahlFehler"
            
                Write-Host $meldung1
                Write-Host $meldung2
                Write-Host $meldung3
                Write-Host ""
                Write-Host "Es wurden keine Dateien umbenannt."
                Write-Host "Wenn alles richtig aussieht, entferne _test aus dem Namen der Starter-Batchdatei."
            
                $protokoll += $meldung1
                $protokoll += $meldung2
                $protokoll += $meldung3
                $protokoll += "Es wurden keine Dateien umbenannt."
            }
            else {
                $meldung1 = "Anzahl umbenannt: $anzahlUmbenannt"
                $meldung2 = "Anzahl übersprungen: $anzahlUebersprungen"
                $meldung3 = "Anzahl Fehler: $anzahlFehler"
            
                Write-Host $meldung1
                Write-Host $meldung2
                Write-Host $meldung3
            
                $protokoll += $meldung1
                $protokoll += $meldung2
                $protokoll += $meldung3
            }
            
            
            # ------------------------------------------------------------
            # Optionale Ausgabe als Logdatei und in die Zwischenablage
            # ------------------------------------------------------------
            
            $protokollText = $protokoll -join [Environment]::NewLine
            $protokollZeitstempel = Get-Date -Format "yyyy-MM-dd_HH-mm-ss"
            $logDatei = Join-Path -Path $basisOrdner -ChildPath "umbenennprotokoll_zeitstempel_setzen_$protokollZeitstempel.log"
            
            if ($logDateiErstellen) {
                try {
                    $protokollText | Set-Content -LiteralPath $logDatei -Encoding UTF8 -ErrorAction Stop
                    Write-Host ""
                    Write-Host "Das Protokoll wurde erstellt:"
                    Write-Host $logDatei
                }
                catch {
                    Write-Host ""
                    Write-Host "FEHLER: Das Protokoll konnte nicht erstellt werden. Grund: $($_.Exception.Message)"
                }
            }
            
            if ($zwischenablageVerwenden) {
                try {
                    $protokollText | Set-Clipboard -ErrorAction Stop
                    Write-Host ""
                    Write-Host "Das Protokoll wurde zusätzlich in die Zwischenablage kopiert."
                }
                catch {
                    Write-Host ""
                    Write-Host "FEHLER: Das Protokoll konnte nicht in die Zwischenablage kopiert werden. Grund: $($_.Exception.Message)"
                }
            }
            
            Write-Host ""
            Read-Host "Drücke Enter zum Beenden"
            

            So funktioniert die Erstellen-Variante

            Die Starter-Batchdatei wechselt mit cd /d "%~dp0" zunächst in ihren eigenen Ordner. Deshalb kann das PowerShell-Skript den aktuellen Arbeitsordner mit Get-Location übernehmen:

            $basisOrdner = (Get-Location).Path

            Get-Date -Format "yyyy-MM-dd_HH-mm-ss" erzeugt den gemeinsamen Zeitstempel für den gesamten Durchlauf. Mit Get-ChildItem -File werden ausschließlich die direkt enthaltenen Dateien ermittelt.

            Die Prüfung mit -match erkennt Dateien, die bereits mit einem passenden Zeitstempel beginnen:

            $zeitstempelMuster = '^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}_'

            Ein bereits vorhandener Zeitstempel wird nicht erneut vorangestellt. Auch ausgeschlossene Endungen und bereits vorhandene Zielnamen werden übersprungen.

            Für jede gültige Datei entsteht ein [PSCustomObject] mit Quelle, altem Namen, neuem Namen und Zielpfad. Im Testmodus werden diese Pläne nur ausgegeben. Im Echtmodus führt Rename-Item die Umbenennung nach der Sicherheitsabfrage aus.

            Starter-Batchdatei für Variante 1

            @echo off
            REM ============================================================
            REM TOPTORIALS OFFICE - PowerShell 7 Grundkurs
            REM Script: 007_zeitstempel_im_dateinamen_starter_batch_test.bat
            REM Beschreibung: Startet die Variante zum Hinzufügen 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-21
            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 (
                pwsh -ExecutionPolicy Bypass -File "007_zeitstempel_im_dateinamen.ps1" -Test
            ) else (
                pwsh -ExecutionPolicy Bypass -File "007_zeitstempel_im_dateinamen.ps1"
            )
            

            %~n0 liefert den Namen der Starter-Batchdatei ohne Dateiendung. find /i "_test" prüft diesen Namen ohne Beachtung der Groß- und Kleinschreibung. Wird _test gefunden, hängt die Batchdatei das Argument -Test an den PowerShell-Aufruf an.

            Variante 2: Zeitstempel im aktuellen Ordner entfernen

            So wird die Variante angewendet

            Kopiere jetzt den Inhalt des Ordners 02_Zeitstempel_aus_Dateinamen_entfernen in den Ordner mit den zuvor umbenannten Dateien.

            Starte auch hier zuerst die Testdatei:

            007_zeitstempel_aus_dateinamen_entfernen_starter_batch_test.bat

            Das Skript erkennt den Zeitstempel am Anfang, entfernt ihn aus dem geplanten Zielnamen und zeigt das Ergebnis an. Für den Echtlauf entfernst du wieder _test, startest die Batchdatei erneut und bestätigst mit JA.

            2026-08-21_14-35-42_Rechnung.pdf  →  Rechnung.pdf
            2026-08-21_14-35-42_Bericht.docx   →  Bericht.docx
            2026-08-21_14-35-42_Foto.jpg       →  Foto.jpg

            Vollständiges PowerShell-7-Skript

            # ============================================================
            # TOPTORIALS OFFICE - PowerShell 7 Grundkurs
            # Script: 007_zeitstempel_aus_dateinamen_entfernen.ps1
            # 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-21
            # ============================================================
            
            param (
                [switch]$Test
            )
            
            Write-Host ""
            
            $basisOrdner = (Get-Location).Path
            $zeitstempelMuster = '^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}_?'
            $zeitstempelRegex = [regex]::new($zeitstempelMuster)
            $ausgeschlosseneEndungen = @(".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
            $logDateiErstellen = $false
            $zwischenablageVerwenden = $false
            
            # Variante 2: Logdatei erstellen
            # $logDateiErstellen = $true
            # $zwischenablageVerwenden = $false
            
            # Variante 3: Nur in die Zwischenablage kopieren
            # $logDateiErstellen = $false
            # $zwischenablageVerwenden = $true
            
            # Variante 4: Logdatei erstellen und in die Zwischenablage kopieren
            # $logDateiErstellen = $true
            # $zwischenablageVerwenden = $true
            
            
            # ------------------------------------------------------------
            # Freien Zielnamen ermitteln
            # Bereits vorhandene und bereits geplante Ziele berücksichtigen.
            # ------------------------------------------------------------
            
            function Get-FreierZielName {
                param (
                    [string]$Ordner,
                    [string]$BasisName,
                    [string]$Erweiterung,
                    [System.Collections.Generic.HashSet[string]]$ReservierteZiele
                )
            
                $zielName = $BasisName + $Erweiterung
                $zielPfad = Join-Path -Path $Ordner -ChildPath $zielName
            
                if (
                    -not (Test-Path -LiteralPath $zielPfad) -and
                    -not $ReservierteZiele.Contains($zielPfad)
                ) {
                    [void]$ReservierteZiele.Add($zielPfad)
            
                    return [PSCustomObject]@{
                        Name = $zielName
                        Pfad = $zielPfad
                    }
                }
            
                $nummer = 1
            
                do {
                    $nummerText = $nummer.ToString("D3")
                    $zielName = $BasisName + "_" + $nummerText + $Erweiterung
                    $zielPfad = Join-Path -Path $Ordner -ChildPath $zielName
                    $nummer++
                }
                while (
                    (Test-Path -LiteralPath $zielPfad) -or
                    $ReservierteZiele.Contains($zielPfad)
                )
            
                [void]$ReservierteZiele.Add($zielPfad)
            
                return [PSCustomObject]@{
                    Name = $zielName
                    Pfad = $zielPfad
                }
            }
            
            
            # ------------------------------------------------------------
            # Modus, Zähler, Protokoll und Pläne vorbereiten
            # ------------------------------------------------------------
            
            if ($Test) {
                $modusText = "TESTMODUS"
            }
            else {
                $modusText = "ECHTMODUS"
            }
            
            $anzahlUmbenannt = 0
            $anzahlUebersprungen = 0
            $anzahlFehler = 0
            $protokoll = @()
            $plaene = @()
            $reservierteZiele = [System.Collections.Generic.HashSet[string]]::new(
                [System.StringComparer]::OrdinalIgnoreCase
            )
            
            $protokoll += "Zeitstempel werden aus Dateinamen im aktuellen Ordner entfernt."
            $protokoll += "Modus: $modusText"
            $protokoll += "Ordner: $basisOrdner"
            $protokoll += "Erkanntes Format: JJJJ-MM-TT_HH-MM-SS_"
            $protokoll += "Ausgeschlossene Endungen: $($ausgeschlosseneEndungen -join ', ')"
            $protokoll += ""
            
            Write-Host "Zeitstempel werden aus Dateinamen im aktuellen Ordner entfernt..."
            Write-Host ""
            Write-Host "Ordner:"
            Write-Host $basisOrdner
            Write-Host ""
            Write-Host "Modus: $modusText"
            Write-Host "Erkanntes Format: JJJJ-MM-TT_HH-MM-SS_"
            Write-Host "Ausgeschlossene Endungen: $($ausgeschlosseneEndungen -join ', ')"
            Write-Host ""
            
            
            # ------------------------------------------------------------
            # Direkt enthaltene Dateien erfassen
            # Unterordner werden nicht durchsucht.
            # ------------------------------------------------------------
            
            try {
                $dateien = @(Get-ChildItem -LiteralPath $basisOrdner -File -Force -ErrorAction Stop)
            }
            catch {
                Write-Host "FEHLER: Der Ordnerinhalt konnte nicht gelesen werden. Grund: $($_.Exception.Message)"
                Write-Host ""
                Read-Host "Drücke Enter zum Beenden"
                exit
            }
            
            
            # ------------------------------------------------------------
            # Dateien prüfen und Umbenennungen planen
            # ------------------------------------------------------------
            
            foreach ($datei in $dateien) {
                if ($ausgeschlosseneEndungen -contains $datei.Extension) {
                    $meldung = "ÜBERSPRUNGEN: `"$($datei.FullName)`" - Dateiendung ist ausgeschlossen"
                    Write-Host $meldung
                    $protokoll += $meldung
                    $anzahlUebersprungen++
                    continue
                }
            
                if ($datei.Name -notmatch $zeitstempelMuster) {
                    $meldung = "ÜBERSPRUNGEN: `"$($datei.FullName)`" - kein Zeitstempel am Anfang gefunden"
                    Write-Host $meldung
                    $protokoll += $meldung
                    $anzahlUebersprungen++
                    continue
                }
            
                $basisName = $zeitstempelRegex.Replace($datei.BaseName, "", 1)
            
                if ([string]::IsNullOrEmpty($basisName)) {
                    $basisName = "Unbenannt"
                }
            
                $freiesZiel = Get-FreierZielName `
                    -Ordner $basisOrdner `
                    -BasisName $basisName `
                    -Erweiterung $datei.Extension `
                    -ReservierteZiele $reservierteZiele
            
                $plaene += [PSCustomObject]@{
                    Quelle    = $datei.FullName
                    AlterName = $datei.Name
                    NeuerName = $freiesZiel.Name
                    Ziel      = $freiesZiel.Pfad
                }
            }
            
            
            # ------------------------------------------------------------
            # Sicherheitsabfrage bei der echten Ausführung
            # ------------------------------------------------------------
            
            if (-not $Test -and $plaene.Count -gt 0) {
                Write-Host ""
                Write-Host "ACHTUNG: Dieses Skript wird Dateien wirklich umbenennen."
                Write-Host ""
            
                $antwort = Read-Host "Zum Fortfahren JA eingeben"
            
                if ($antwort -ne "JA") {
                    Write-Host ""
                    Write-Host "Abgebrochen."
                    Write-Host ""
                    Read-Host "Drücke Enter zum Beenden"
                    exit
                }
            
                Write-Host ""
            }
            
            
            # ------------------------------------------------------------
            # Geplante Umbenennungen ausführen
            # ------------------------------------------------------------
            
            foreach ($plan in $plaene) {
                if ($Test) {
                    $meldung = "WÜRDE UMBENENNEN:`nQuelle: `"$($plan.Quelle)`"`nZiel:   `"$($plan.Ziel)`"`n---"
                    Write-Host $meldung
                    $protokoll += $meldung
                    $anzahlUmbenannt++
                    continue
                }
            
                try {
                    Rename-Item -LiteralPath $plan.Quelle -NewName $plan.NeuerName -ErrorAction Stop
            
                    $meldung = "UMBENANNT:`nQuelle: `"$($plan.Quelle)`"`nZiel:   `"$($plan.Ziel)`"`n---"
                    Write-Host $meldung
                    $protokoll += $meldung
                    $anzahlUmbenannt++
                }
                catch {
                    $meldung = "FEHLER: `"$($plan.Quelle)`" konnte nicht umbenannt werden. Grund: $($_.Exception.Message)"
                    Write-Host $meldung
                    $protokoll += $meldung
                    $anzahlFehler++
                }
            }
            
            
            # ------------------------------------------------------------
            # Zusammenfassung
            # ------------------------------------------------------------
            
            $protokoll += ""
            $protokoll += "Fertig."
            
            Write-Host ""
            Write-Host "Fertig."
            Write-Host ""
            
            if ($Test) {
                $meldung1 = "Anzahl Dateien, die umbenannt würden: $anzahlUmbenannt"
                $meldung2 = "Anzahl übersprungen: $anzahlUebersprungen"
                $meldung3 = "Anzahl Fehler: $anzahlFehler"
            
                Write-Host $meldung1
                Write-Host $meldung2
                Write-Host $meldung3
                Write-Host ""
                Write-Host "Es wurden keine Dateien umbenannt."
                Write-Host "Wenn alles richtig aussieht, entferne _test aus dem Namen der Starter-Batchdatei."
            
                $protokoll += $meldung1
                $protokoll += $meldung2
                $protokoll += $meldung3
                $protokoll += "Es wurden keine Dateien umbenannt."
            }
            else {
                $meldung1 = "Anzahl umbenannt: $anzahlUmbenannt"
                $meldung2 = "Anzahl übersprungen: $anzahlUebersprungen"
                $meldung3 = "Anzahl Fehler: $anzahlFehler"
            
                Write-Host $meldung1
                Write-Host $meldung2
                Write-Host $meldung3
            
                $protokoll += $meldung1
                $protokoll += $meldung2
                $protokoll += $meldung3
            }
            
            
            # ------------------------------------------------------------
            # Optionale Ausgabe als Logdatei und in die Zwischenablage
            # ------------------------------------------------------------
            
            $protokollText = $protokoll -join [Environment]::NewLine
            $protokollZeitstempel = Get-Date -Format "yyyy-MM-dd_HH-mm-ss"
            $logDatei = Join-Path -Path $basisOrdner -ChildPath "umbenennprotokoll_zeitstempel_entfernen_$protokollZeitstempel.log"
            
            if ($logDateiErstellen) {
                try {
                    $protokollText | Set-Content -LiteralPath $logDatei -Encoding UTF8 -ErrorAction Stop
                    Write-Host ""
                    Write-Host "Das Protokoll wurde erstellt:"
                    Write-Host $logDatei
                }
                catch {
                    Write-Host ""
                    Write-Host "FEHLER: Das Protokoll konnte nicht erstellt werden. Grund: $($_.Exception.Message)"
                }
            }
            
            if ($zwischenablageVerwenden) {
                try {
                    $protokollText | Set-Clipboard -ErrorAction Stop
                    Write-Host ""
                    Write-Host "Das Protokoll wurde zusätzlich in die Zwischenablage kopiert."
                }
                catch {
                    Write-Host ""
                    Write-Host "FEHLER: Das Protokoll konnte nicht in die Zwischenablage kopiert werden. Grund: $($_.Exception.Message)"
                }
            }
            
            Write-Host ""
            Read-Host "Drücke Enter zum Beenden"
            

            So funktioniert die Entfernen-Variante

            Das Muster für das Entfernen ist am Ende etwas flexibler. Der letzte Unterstrich ist mit _? optional:

            $zeitstempelMuster = '^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}_?'

            Dadurch werden sowohl 2026-08-21_14-35-42_Rechnung.pdf als auch 2026-08-21_14-35-42Rechnung.pdf erkannt. Mit [regex]::new wird aus dem Muster ein regulärer Ausdruck, dessen Methode Replace genau den ersten Treffer am Anfang des Basisnamens entfernt:

            $basisName = $zeitstempelRegex.Replace($datei.BaseName, "", 1)

            Die Dateiendung bleibt getrennt erhalten und wird später wieder angefügt. Sollte nach dem Entfernen kein Basisname übrig bleiben, verwendet das Skript Unbenannt.

            Besonders wichtig ist die Funktion Get-FreierZielName. Existiert der ursprüngliche Name bereits, wird er nicht überschrieben. Stattdessen sucht die Funktion automatisch einen freien Namen mit einer dreistelligen Nummer:

            Rechnung.pdf
            Rechnung_001.pdf
            Rechnung_002.pdf

            Das HashSet[string] in $reservierteZiele berücksichtigt zusätzlich Namen, die während desselben Durchlaufs bereits geplant wurden. Damit können auch mehrere Quelldateien nicht versehentlich dasselbe Ziel erhalten.

            Starter-Batchdatei für Variante 2

            @echo off
            REM ============================================================
            REM TOPTORIALS OFFICE - PowerShell 7 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-21
            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 (
                pwsh -ExecutionPolicy Bypass -File "007_zeitstempel_aus_dateinamen_entfernen.ps1" -Test
            ) else (
                pwsh -ExecutionPolicy Bypass -File "007_zeitstempel_aus_dateinamen_entfernen.ps1"
            )
            

            Variante 3: Zeitstempel über „Senden an“ erstellen

            SendTo-Verknüpfung einrichten

            1. Mit Windows + R den Dialog Ausführen öffnen.
            2. shell:sendto eingeben und mit Enter bestätigen.
            3. Eine Verknüpfung zur Datei 007_sendto_zeitstempel_im_dateinamen_starter_batch_test.bat anlegen.
            4. Der Verknüpfung einen verständlichen Namen geben, zum Beispiel Zeitstempel hinzufügen - TEST.

            Die Starter-Batchdatei und das PowerShell-Skript selbst bleiben gemeinsam im Ordner:

            C:\TOPTORIALS_CODE\010_PowerShell_7_Grundkurs\007_Zeitstempel_im_Dateinamen\03_SendTo_Zeitstempel_im_Dateinamen

            So wird die Variante angewendet

            Markiere im Windows-Explorer eine oder mehrere Dateien, einen oder mehrere Ordner oder eine Mischung daraus. Öffne anschließend mit der rechten Maustaste das Kontextmenü und wähle Senden an → Zeitstempel hinzufügen – TEST.

            Bei einer direkt übergebenen Datei wird genau diese Datei verarbeitet. Bei einem übergebenen Ordner verarbeitet das Skript die direkt enthaltenen Dateien. Unterordner werden nicht durchsucht.

            Wenn die Testausgabe stimmt, entfernst du _test aus dem Namen der Original-Starter-Batchdatei. Passe danach den Namen der SendTo-Verknüpfung an und teste den Echtlauf zunächst mit unwichtigen Dateikopien.

            Vollständiges PowerShell-7-Skript

            # ============================================================
            # TOPTORIALS OFFICE - PowerShell 7 Grundkurs
            # Script: 007_sendto_zeitstempel_im_dateinamen.ps1
            # 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-21
            # ============================================================
            
            param (
                [switch]$Test,
            
                [Parameter(ValueFromRemainingArguments = $true)]
                [string[]]$Elemente
            )
            
            Write-Host ""
            
            
            # ------------------------------------------------------------
            # Prüfen, ob mindestens ein Element übergeben wurde
            # ------------------------------------------------------------
            
            if (-not $Elemente -or $Elemente.Count -eq 0) {
                Write-Host "Bitte sende eine oder mehrere Dateien oder Ordner an dieses Skript."
                Write-Host ""
                Write-Host "Du kannst im Explorer Dateien oder Ordner markieren,"
                Write-Host "rechts anklicken und anschließend 'Senden an' verwenden."
                Write-Host ""
                Read-Host "Drücke Enter zum Beenden"
                exit
            }
            
            $zeitstempel = Get-Date -Format "yyyy-MM-dd_HH-mm-ss"
            $zeitstempelMuster = '^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}_'
            $ausgeschlosseneEndungen = @(".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
            $logDateiErstellen = $false
            $zwischenablageVerwenden = $false
            
            # Variante 2: Logdatei erstellen
            # $logDateiErstellen = $true
            # $zwischenablageVerwenden = $false
            
            # Variante 3: Nur in die Zwischenablage kopieren
            # $logDateiErstellen = $false
            # $zwischenablageVerwenden = $true
            
            # Variante 4: Logdatei erstellen und in die Zwischenablage kopieren
            # $logDateiErstellen = $true
            # $zwischenablageVerwenden = $true
            
            
            # ------------------------------------------------------------
            # Modus, Zähler, Protokoll und Pläne vorbereiten
            # ------------------------------------------------------------
            
            if ($Test) {
                $modusText = "TESTMODUS"
            }
            else {
                $modusText = "ECHTMODUS"
            }
            
            $anzahlUmbenannt = 0
            $anzahlUebersprungen = 0
            $anzahlFehler = 0
            $anzahlOrdner = 0
            $anzahlEinzeldateien = 0
            $protokoll = @()
            $plaene = @()
            $dateien = @()
            $bereitsErfassteDateien = @{}
            
            $protokoll += "Übergebene Dateien und Dateien in übergebenen Ordnern erhalten einen Zeitstempel."
            $protokoll += "Modus: $modusText"
            $protokoll += "Zeitstempel: $zeitstempel"
            $protokoll += "Ausgeschlossene Endungen: $($ausgeschlosseneEndungen -join ', ')"
            $protokoll += ""
            
            Write-Host "Übergebene Dateien und Dateien in übergebenen Ordnern erhalten einen Zeitstempel..."
            Write-Host ""
            Write-Host "Zeitstempel: $zeitstempel"
            Write-Host "Modus: $modusText"
            Write-Host "Ausgeschlossene Endungen: $($ausgeschlosseneEndungen -join ', ')"
            Write-Host ""
            
            
            # ------------------------------------------------------------
            # Übergebene Dateien und Ordner erfassen
            # Ordnerinhalte werden nicht rekursiv durchsucht.
            # ------------------------------------------------------------
            
            $eindeutigeElemente = @($Elemente | Select-Object -Unique)
            
            foreach ($elementPfad in $eindeutigeElemente) {
                if (-not (Test-Path -LiteralPath $elementPfad)) {
                    $meldung = "ÜBERSPRUNGEN: `"$elementPfad`" - Datei oder Ordner wurde nicht gefunden"
                    Write-Host $meldung
                    $protokoll += $meldung
                    $anzahlUebersprungen++
                    continue
                }
            
                try {
                    $element = Get-Item -LiteralPath $elementPfad -Force -ErrorAction Stop
            
                    if ($element.PSIsContainer) {
                        if ($null -eq $element.Parent) {
                            $meldung = "ÜBERSPRUNGEN: Eine Laufwerkswurzel wird aus Sicherheitsgründen nicht verarbeitet: `"$($element.FullName)`""
                            Write-Host $meldung
                            $protokoll += $meldung
                            $anzahlUebersprungen++
                            continue
                        }
            
                        Write-Host "Ordner wird verarbeitet:"
                        Write-Host $element.FullName
                        Write-Host ""
            
                        $protokoll += "Verarbeiteter Ordner: $($element.FullName)"
                        $anzahlOrdner++
            
                        $ordnerDateien = @(
                            Get-ChildItem -LiteralPath $element.FullName -File -Force -ErrorAction Stop
                        )
            
                        foreach ($datei in $ordnerDateien) {
                            $vergleichsPfad = $datei.FullName.ToLowerInvariant()
            
                            if (-not $bereitsErfassteDateien.ContainsKey($vergleichsPfad)) {
                                $bereitsErfassteDateien[$vergleichsPfad] = $true
                                $dateien += $datei
                            }
                        }
            
                        $protokoll += ""
                        continue
                    }
            
                    $anzahlEinzeldateien++
                    $vergleichsPfad = $element.FullName.ToLowerInvariant()
            
                    if (-not $bereitsErfassteDateien.ContainsKey($vergleichsPfad)) {
                        $bereitsErfassteDateien[$vergleichsPfad] = $true
                        $dateien += $element
                    }
                }
                catch {
                    $meldung = "FEHLER: `"$elementPfad`" konnte nicht eingelesen werden. Grund: $($_.Exception.Message)"
                    Write-Host $meldung
                    $protokoll += $meldung
                    $anzahlFehler++
                }
            }
            
            
            # ------------------------------------------------------------
            # Dateien prüfen und Umbenennungen planen
            # ------------------------------------------------------------
            
            foreach ($datei in $dateien) {
                if ($ausgeschlosseneEndungen -contains $datei.Extension) {
                    $meldung = "ÜBERSPRUNGEN: `"$($datei.FullName)`" - Dateiendung ist ausgeschlossen"
                    Write-Host $meldung
                    $protokoll += $meldung
                    $anzahlUebersprungen++
                    continue
                }
            
                if ($datei.Name -match $zeitstempelMuster) {
                    $meldung = "ÜBERSPRUNGEN: `"$($datei.FullName)`" - Zeitstempel ist bereits vorhanden"
                    Write-Host $meldung
                    $protokoll += $meldung
                    $anzahlUebersprungen++
                    continue
                }
            
                $neuerName = $zeitstempel + "_" + $datei.Name
                $zielPfad = Join-Path -Path $datei.DirectoryName -ChildPath $neuerName
            
                if (Test-Path -LiteralPath $zielPfad) {
                    $meldung = "ÜBERSPRUNGEN: `"$($datei.FullName)`" - Zieldatei existiert bereits: `"$zielPfad`""
                    Write-Host $meldung
                    $protokoll += $meldung
                    $anzahlUebersprungen++
                    continue
                }
            
                $plaene += [PSCustomObject]@{
                    Quelle    = $datei.FullName
                    AlterName = $datei.Name
                    NeuerName = $neuerName
                    Ziel      = $zielPfad
                }
            }
            
            
            # ------------------------------------------------------------
            # Sicherheitsabfrage bei der echten Ausführung
            # ------------------------------------------------------------
            
            if (-not $Test -and $plaene.Count -gt 0) {
                Write-Host ""
                Write-Host "ACHTUNG: Dieses Skript wird Dateien wirklich umbenennen."
                Write-Host ""
            
                $antwort = Read-Host "Zum Fortfahren JA eingeben"
            
                if ($antwort -ne "JA") {
                    Write-Host ""
                    Write-Host "Abgebrochen."
                    Write-Host ""
                    Read-Host "Drücke Enter zum Beenden"
                    exit
                }
            
                Write-Host ""
            }
            
            
            # ------------------------------------------------------------
            # Geplante Umbenennungen ausführen
            # ------------------------------------------------------------
            
            foreach ($plan in $plaene) {
                if ($Test) {
                    $meldung = "WÜRDE UMBENENNEN:`nQuelle: `"$($plan.Quelle)`"`nZiel:   `"$($plan.Ziel)`"`n---"
                    Write-Host $meldung
                    $protokoll += $meldung
                    $anzahlUmbenannt++
                    continue
                }
            
                try {
                    Rename-Item -LiteralPath $plan.Quelle -NewName $plan.NeuerName -ErrorAction Stop
            
                    $meldung = "UMBENANNT:`nQuelle: `"$($plan.Quelle)`"`nZiel:   `"$($plan.Ziel)`"`n---"
                    Write-Host $meldung
                    $protokoll += $meldung
                    $anzahlUmbenannt++
                }
                catch {
                    $meldung = "FEHLER: `"$($plan.Quelle)`" konnte nicht umbenannt werden. Grund: $($_.Exception.Message)"
                    Write-Host $meldung
                    $protokoll += $meldung
                    $anzahlFehler++
                }
            }
            
            
            # ------------------------------------------------------------
            # Zusammenfassung
            # ------------------------------------------------------------
            
            $protokoll += ""
            $protokoll += "Fertig."
            
            Write-Host ""
            Write-Host "Fertig."
            Write-Host ""
            
            $meldungDateien = "Übergebene Einzeldateien: $anzahlEinzeldateien"
            $meldungOrdner = "Verarbeitete Ordner: $anzahlOrdner"
            
            Write-Host $meldungDateien
            Write-Host $meldungOrdner
            $protokoll += $meldungDateien
            $protokoll += $meldungOrdner
            
            if ($Test) {
                $meldung1 = "Anzahl Dateien, die umbenannt würden: $anzahlUmbenannt"
                $meldung2 = "Anzahl übersprungen: $anzahlUebersprungen"
                $meldung3 = "Anzahl Fehler: $anzahlFehler"
            
                Write-Host $meldung1
                Write-Host $meldung2
                Write-Host $meldung3
                Write-Host ""
                Write-Host "Es wurden keine Dateien umbenannt."
                Write-Host "Wenn alles richtig aussieht, entferne _test aus dem Namen der Starter-Batchdatei."
            
                $protokoll += $meldung1
                $protokoll += $meldung2
                $protokoll += $meldung3
                $protokoll += "Es wurden keine Dateien umbenannt."
            }
            else {
                $meldung1 = "Anzahl umbenannt: $anzahlUmbenannt"
                $meldung2 = "Anzahl übersprungen: $anzahlUebersprungen"
                $meldung3 = "Anzahl Fehler: $anzahlFehler"
            
                Write-Host $meldung1
                Write-Host $meldung2
                Write-Host $meldung3
            
                $protokoll += $meldung1
                $protokoll += $meldung2
                $protokoll += $meldung3
            }
            
            
            # ------------------------------------------------------------
            # Optionale Ausgabe als Logdatei und in die Zwischenablage
            # ------------------------------------------------------------
            
            $protokollText = $protokoll -join [Environment]::NewLine
            $protokollZeitstempel = Get-Date -Format "yyyy-MM-dd_HH-mm-ss"
            $logDatei = Join-Path -Path $PSScriptRoot -ChildPath "umbenennprotokoll_sendto_zeitstempel_setzen_$protokollZeitstempel.log"
            
            if ($logDateiErstellen) {
                try {
                    $protokollText | Set-Content -LiteralPath $logDatei -Encoding UTF8 -ErrorAction Stop
                    Write-Host ""
                    Write-Host "Das Protokoll wurde erstellt:"
                    Write-Host $logDatei
                }
                catch {
                    Write-Host ""
                    Write-Host "FEHLER: Das Protokoll konnte nicht erstellt werden. Grund: $($_.Exception.Message)"
                }
            }
            
            if ($zwischenablageVerwenden) {
                try {
                    $protokollText | Set-Clipboard -ErrorAction Stop
                    Write-Host ""
                    Write-Host "Das Protokoll wurde zusätzlich in die Zwischenablage kopiert."
                }
                catch {
                    Write-Host ""
                    Write-Host "FEHLER: Das Protokoll konnte nicht in die Zwischenablage kopiert werden. Grund: $($_.Exception.Message)"
                }
            }
            
            Write-Host ""
            Read-Host "Drücke Enter zum Beenden"
            

            So funktioniert die SendTo-Erstellen-Variante

            Der Parameterblock nimmt neben dem Schalter $Test alle weiteren Argumente in $Elemente entgegen:

            param (
                [switch]$Test,
            
                [Parameter(ValueFromRemainingArguments = $true)]
                [string[]]$Elemente
            )

            [string[]] bedeutet, dass mehrere Pfade gespeichert werden können. Die Batchdatei übergibt mit %* alle vom Explorer gelieferten Argumente.

            Jeder Pfad wird mit Test-Path geprüft und anschließend mit Get-Item als Datei oder Ordner eingelesen. $element.PSIsContainer unterscheidet Ordner von einzelnen Dateien. Laufwerkswurzeln werden aus Sicherheitsgründen nicht verarbeitet.

            Die Hashtable $bereitsErfassteDateien verhindert eine doppelte Verarbeitung. Das ist beispielsweise wichtig, wenn eine Datei direkt ausgewählt und gleichzeitig ihr übergeordneter Ordner übergeben wurde.

            Nach dem Erfassen arbeitet die bekannte Prüf- und Planungslogik mit allen eindeutigen Dateien weiter. Jede Datei erhält den Zeitstempel in ihrem eigenen Ordner.

            Starter-Batchdatei für Variante 3

            @echo off
            REM ============================================================
            REM TOPTORIALS OFFICE - PowerShell 7 Grundkurs
            REM Script: 007_sendto_zeitstempel_im_dateinamen_starter_batch_test.bat
            REM Beschreibung: Startet die SendTo-Variante zum Hinzufügen 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-21
            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 (
                pwsh -ExecutionPolicy Bypass -File "007_sendto_zeitstempel_im_dateinamen.ps1" -Test %*
            ) else (
                pwsh -ExecutionPolicy Bypass -File "007_sendto_zeitstempel_im_dateinamen.ps1" %*
            )
            

            Der entscheidende Unterschied zu den ersten beiden Starter-Batchdateien ist %*. Dieser Ausdruck gibt alle an die Batchdatei übergebenen Argumente an das PowerShell-Skript weiter.

            Variante 4: Zeitstempel über „Senden an“ entfernen

            SendTo-Verknüpfung einrichten

            Lege im mit shell:sendto geöffneten Ordner zusätzlich eine Verknüpfung zu dieser Starter-Batchdatei an:

            C:\TOPTORIALS_CODE\010_PowerShell_7_Grundkurs\007_Zeitstempel_im_Dateinamen\04_SendTo_Zeitstempel_aus_Dateinamen_entfernen\007_sendto_zeitstempel_aus_dateinamen_entfernen_starter_batch_test.bat

            Ein passender Name für die Verknüpfung ist beispielsweise Zeitstempel entfernen - TEST.

            So wird die Variante angewendet

            Markiere Dateien oder Ordner mit Zeitstempeln im Windows-Explorer. Wähle danach Rechtsklick → Senden an → Zeitstempel entfernen – TEST.

            Das Skript zeigt für jede passende Datei den Namen an, der nach dem Entfernen des Zeitstempels entstehen würde. Dateien ohne erkannten Zeitstempel sowie die ausgeschlossenen Skript- und Protokolldateien werden übersprungen.

            Für den Echtlauf entfernst du _test aus dem Namen der Original-Starter-Batchdatei, passt den Namen der Verknüpfung an und bestätigst die Ausführung mit JA.

            Vollständiges PowerShell-7-Skript

            # ============================================================
            # TOPTORIALS OFFICE - PowerShell 7 Grundkurs
            # Script: 007_sendto_zeitstempel_aus_dateinamen_entfernen.ps1
            # 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-21
            # ============================================================
            
            param (
                [switch]$Test,
            
                [Parameter(ValueFromRemainingArguments = $true)]
                [string[]]$Elemente
            )
            
            Write-Host ""
            
            
            # ------------------------------------------------------------
            # Prüfen, ob mindestens ein Element übergeben wurde
            # ------------------------------------------------------------
            
            if (-not $Elemente -or $Elemente.Count -eq 0) {
                Write-Host "Bitte sende eine oder mehrere Dateien oder Ordner an dieses Skript."
                Write-Host ""
                Write-Host "Du kannst im Explorer Dateien oder Ordner markieren,"
                Write-Host "rechts anklicken und anschließend 'Senden an' verwenden."
                Write-Host ""
                Read-Host "Drücke Enter zum Beenden"
                exit
            }
            
            $zeitstempelMuster = '^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}_?'
            $zeitstempelRegex = [regex]::new($zeitstempelMuster)
            $ausgeschlosseneEndungen = @(".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
            $logDateiErstellen = $false
            $zwischenablageVerwenden = $false
            
            # Variante 2: Logdatei erstellen
            # $logDateiErstellen = $true
            # $zwischenablageVerwenden = $false
            
            # Variante 3: Nur in die Zwischenablage kopieren
            # $logDateiErstellen = $false
            # $zwischenablageVerwenden = $true
            
            # Variante 4: Logdatei erstellen und in die Zwischenablage kopieren
            # $logDateiErstellen = $true
            # $zwischenablageVerwenden = $true
            
            
            # ------------------------------------------------------------
            # Freien Zielnamen ermitteln
            # Bereits vorhandene und bereits geplante Ziele berücksichtigen.
            # ------------------------------------------------------------
            
            function Get-FreierZielName {
                param (
                    [string]$Ordner,
                    [string]$BasisName,
                    [string]$Erweiterung,
                    [System.Collections.Generic.HashSet[string]]$ReservierteZiele
                )
            
                $zielName = $BasisName + $Erweiterung
                $zielPfad = Join-Path -Path $Ordner -ChildPath $zielName
            
                if (
                    -not (Test-Path -LiteralPath $zielPfad) -and
                    -not $ReservierteZiele.Contains($zielPfad)
                ) {
                    [void]$ReservierteZiele.Add($zielPfad)
            
                    return [PSCustomObject]@{
                        Name = $zielName
                        Pfad = $zielPfad
                    }
                }
            
                $nummer = 1
            
                do {
                    $nummerText = $nummer.ToString("D3")
                    $zielName = $BasisName + "_" + $nummerText + $Erweiterung
                    $zielPfad = Join-Path -Path $Ordner -ChildPath $zielName
                    $nummer++
                }
                while (
                    (Test-Path -LiteralPath $zielPfad) -or
                    $ReservierteZiele.Contains($zielPfad)
                )
            
                [void]$ReservierteZiele.Add($zielPfad)
            
                return [PSCustomObject]@{
                    Name = $zielName
                    Pfad = $zielPfad
                }
            }
            
            
            # ------------------------------------------------------------
            # Modus, Zähler, Protokoll und Pläne vorbereiten
            # ------------------------------------------------------------
            
            if ($Test) {
                $modusText = "TESTMODUS"
            }
            else {
                $modusText = "ECHTMODUS"
            }
            
            $anzahlUmbenannt = 0
            $anzahlUebersprungen = 0
            $anzahlFehler = 0
            $anzahlOrdner = 0
            $anzahlEinzeldateien = 0
            $protokoll = @()
            $plaene = @()
            $dateien = @()
            $bereitsErfassteDateien = @{}
            $reservierteZiele = [System.Collections.Generic.HashSet[string]]::new(
                [System.StringComparer]::OrdinalIgnoreCase
            )
            
            $protokoll += "Zeitstempel werden aus übergebenen Dateien und Dateien in übergebenen Ordnern entfernt."
            $protokoll += "Modus: $modusText"
            $protokoll += "Erkanntes Format: JJJJ-MM-TT_HH-MM-SS_"
            $protokoll += "Ausgeschlossene Endungen: $($ausgeschlosseneEndungen -join ', ')"
            $protokoll += ""
            
            Write-Host "Zeitstempel werden aus übergebenen Dateien und Dateien in übergebenen Ordnern entfernt..."
            Write-Host ""
            Write-Host "Modus: $modusText"
            Write-Host "Erkanntes Format: JJJJ-MM-TT_HH-MM-SS_"
            Write-Host "Ausgeschlossene Endungen: $($ausgeschlosseneEndungen -join ', ')"
            Write-Host ""
            
            
            # ------------------------------------------------------------
            # Übergebene Dateien und Ordner erfassen
            # Ordnerinhalte werden nicht rekursiv durchsucht.
            # ------------------------------------------------------------
            
            $eindeutigeElemente = @($Elemente | Select-Object -Unique)
            
            foreach ($elementPfad in $eindeutigeElemente) {
                if (-not (Test-Path -LiteralPath $elementPfad)) {
                    $meldung = "ÜBERSPRUNGEN: `"$elementPfad`" - Datei oder Ordner wurde nicht gefunden"
                    Write-Host $meldung
                    $protokoll += $meldung
                    $anzahlUebersprungen++
                    continue
                }
            
                try {
                    $element = Get-Item -LiteralPath $elementPfad -Force -ErrorAction Stop
            
                    if ($element.PSIsContainer) {
                        if ($null -eq $element.Parent) {
                            $meldung = "ÜBERSPRUNGEN: Eine Laufwerkswurzel wird aus Sicherheitsgründen nicht verarbeitet: `"$($element.FullName)`""
                            Write-Host $meldung
                            $protokoll += $meldung
                            $anzahlUebersprungen++
                            continue
                        }
            
                        Write-Host "Ordner wird verarbeitet:"
                        Write-Host $element.FullName
                        Write-Host ""
            
                        $protokoll += "Verarbeiteter Ordner: $($element.FullName)"
                        $anzahlOrdner++
            
                        $ordnerDateien = @(
                            Get-ChildItem -LiteralPath $element.FullName -File -Force -ErrorAction Stop
                        )
            
                        foreach ($datei in $ordnerDateien) {
                            $vergleichsPfad = $datei.FullName.ToLowerInvariant()
            
                            if (-not $bereitsErfassteDateien.ContainsKey($vergleichsPfad)) {
                                $bereitsErfassteDateien[$vergleichsPfad] = $true
                                $dateien += $datei
                            }
                        }
            
                        $protokoll += ""
                        continue
                    }
            
                    $anzahlEinzeldateien++
                    $vergleichsPfad = $element.FullName.ToLowerInvariant()
            
                    if (-not $bereitsErfassteDateien.ContainsKey($vergleichsPfad)) {
                        $bereitsErfassteDateien[$vergleichsPfad] = $true
                        $dateien += $element
                    }
                }
                catch {
                    $meldung = "FEHLER: `"$elementPfad`" konnte nicht eingelesen werden. Grund: $($_.Exception.Message)"
                    Write-Host $meldung
                    $protokoll += $meldung
                    $anzahlFehler++
                }
            }
            
            
            # ------------------------------------------------------------
            # Dateien prüfen und Umbenennungen planen
            # ------------------------------------------------------------
            
            foreach ($datei in $dateien) {
                if ($ausgeschlosseneEndungen -contains $datei.Extension) {
                    $meldung = "ÜBERSPRUNGEN: `"$($datei.FullName)`" - Dateiendung ist ausgeschlossen"
                    Write-Host $meldung
                    $protokoll += $meldung
                    $anzahlUebersprungen++
                    continue
                }
            
                if ($datei.Name -notmatch $zeitstempelMuster) {
                    $meldung = "ÜBERSPRUNGEN: `"$($datei.FullName)`" - kein Zeitstempel am Anfang gefunden"
                    Write-Host $meldung
                    $protokoll += $meldung
                    $anzahlUebersprungen++
                    continue
                }
            
                $basisName = $zeitstempelRegex.Replace($datei.BaseName, "", 1)
            
                if ([string]::IsNullOrEmpty($basisName)) {
                    $basisName = "Unbenannt"
                }
            
                $freiesZiel = Get-FreierZielName `
                    -Ordner $datei.DirectoryName `
                    -BasisName $basisName `
                    -Erweiterung $datei.Extension `
                    -ReservierteZiele $reservierteZiele
            
                $plaene += [PSCustomObject]@{
                    Quelle    = $datei.FullName
                    AlterName = $datei.Name
                    NeuerName = $freiesZiel.Name
                    Ziel      = $freiesZiel.Pfad
                }
            }
            
            
            # ------------------------------------------------------------
            # Sicherheitsabfrage bei der echten Ausführung
            # ------------------------------------------------------------
            
            if (-not $Test -and $plaene.Count -gt 0) {
                Write-Host ""
                Write-Host "ACHTUNG: Dieses Skript wird Dateien wirklich umbenennen."
                Write-Host ""
            
                $antwort = Read-Host "Zum Fortfahren JA eingeben"
            
                if ($antwort -ne "JA") {
                    Write-Host ""
                    Write-Host "Abgebrochen."
                    Write-Host ""
                    Read-Host "Drücke Enter zum Beenden"
                    exit
                }
            
                Write-Host ""
            }
            
            
            # ------------------------------------------------------------
            # Geplante Umbenennungen ausführen
            # ------------------------------------------------------------
            
            foreach ($plan in $plaene) {
                if ($Test) {
                    $meldung = "WÜRDE UMBENENNEN:`nQuelle: `"$($plan.Quelle)`"`nZiel:   `"$($plan.Ziel)`"`n---"
                    Write-Host $meldung
                    $protokoll += $meldung
                    $anzahlUmbenannt++
                    continue
                }
            
                try {
                    Rename-Item -LiteralPath $plan.Quelle -NewName $plan.NeuerName -ErrorAction Stop
            
                    $meldung = "UMBENANNT:`nQuelle: `"$($plan.Quelle)`"`nZiel:   `"$($plan.Ziel)`"`n---"
                    Write-Host $meldung
                    $protokoll += $meldung
                    $anzahlUmbenannt++
                }
                catch {
                    $meldung = "FEHLER: `"$($plan.Quelle)`" konnte nicht umbenannt werden. Grund: $($_.Exception.Message)"
                    Write-Host $meldung
                    $protokoll += $meldung
                    $anzahlFehler++
                }
            }
            
            
            # ------------------------------------------------------------
            # Zusammenfassung
            # ------------------------------------------------------------
            
            $protokoll += ""
            $protokoll += "Fertig."
            
            Write-Host ""
            Write-Host "Fertig."
            Write-Host ""
            
            $meldungDateien = "Übergebene Einzeldateien: $anzahlEinzeldateien"
            $meldungOrdner = "Verarbeitete Ordner: $anzahlOrdner"
            
            Write-Host $meldungDateien
            Write-Host $meldungOrdner
            $protokoll += $meldungDateien
            $protokoll += $meldungOrdner
            
            if ($Test) {
                $meldung1 = "Anzahl Dateien, die umbenannt würden: $anzahlUmbenannt"
                $meldung2 = "Anzahl übersprungen: $anzahlUebersprungen"
                $meldung3 = "Anzahl Fehler: $anzahlFehler"
            
                Write-Host $meldung1
                Write-Host $meldung2
                Write-Host $meldung3
                Write-Host ""
                Write-Host "Es wurden keine Dateien umbenannt."
                Write-Host "Wenn alles richtig aussieht, entferne _test aus dem Namen der Starter-Batchdatei."
            
                $protokoll += $meldung1
                $protokoll += $meldung2
                $protokoll += $meldung3
                $protokoll += "Es wurden keine Dateien umbenannt."
            }
            else {
                $meldung1 = "Anzahl umbenannt: $anzahlUmbenannt"
                $meldung2 = "Anzahl übersprungen: $anzahlUebersprungen"
                $meldung3 = "Anzahl Fehler: $anzahlFehler"
            
                Write-Host $meldung1
                Write-Host $meldung2
                Write-Host $meldung3
            
                $protokoll += $meldung1
                $protokoll += $meldung2
                $protokoll += $meldung3
            }
            
            
            # ------------------------------------------------------------
            # Optionale Ausgabe als Logdatei und in die Zwischenablage
            # ------------------------------------------------------------
            
            $protokollText = $protokoll -join [Environment]::NewLine
            $protokollZeitstempel = Get-Date -Format "yyyy-MM-dd_HH-mm-ss"
            $logDatei = Join-Path -Path $PSScriptRoot -ChildPath "umbenennprotokoll_sendto_zeitstempel_entfernen_$protokollZeitstempel.log"
            
            if ($logDateiErstellen) {
                try {
                    $protokollText | Set-Content -LiteralPath $logDatei -Encoding UTF8 -ErrorAction Stop
                    Write-Host ""
                    Write-Host "Das Protokoll wurde erstellt:"
                    Write-Host $logDatei
                }
                catch {
                    Write-Host ""
                    Write-Host "FEHLER: Das Protokoll konnte nicht erstellt werden. Grund: $($_.Exception.Message)"
                }
            }
            
            if ($zwischenablageVerwenden) {
                try {
                    $protokollText | Set-Clipboard -ErrorAction Stop
                    Write-Host ""
                    Write-Host "Das Protokoll wurde zusätzlich in die Zwischenablage kopiert."
                }
                catch {
                    Write-Host ""
                    Write-Host "FEHLER: Das Protokoll konnte nicht in die Zwischenablage kopiert werden. Grund: $($_.Exception.Message)"
                }
            }
            
            Write-Host ""
            Read-Host "Drücke Enter zum Beenden"
            

            So funktioniert die SendTo-Entfernen-Variante

            Diese Variante verbindet die Argumentverarbeitung der SendTo-Erstellen-Version mit der Funktion Get-FreierZielName aus der zweiten Variante.

            Zuerst werden alle übergebenen Dateien und die direkt enthaltenen Dateien übergebener Ordner eindeutig erfasst. Danach prüft der reguläre Ausdruck, ob der jeweilige Dateiname mit einem passenden Zeitstempel beginnt.

            Der Zeitstempel wird nur aus $datei.BaseName entfernt. Die ursprüngliche Dateiendung aus $datei.Extension bleibt erhalten. Get-FreierZielName sucht anschließend im jeweiligen $datei.DirectoryName nach einem freien Ziel.

            Weil ein gemeinsames HashSet[string] alle geplanten Zielpfade speichert, funktioniert der Schutz auch dann, wenn gleichzeitig Dateien aus mehreren unterschiedlichen Ordnern verarbeitet werden.

            Starter-Batchdatei für Variante 4

            @echo off
            REM ============================================================
            REM TOPTORIALS OFFICE - PowerShell 7 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-21
            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 (
                pwsh -ExecutionPolicy Bypass -File "007_sendto_zeitstempel_aus_dateinamen_entfernen.ps1" -Test %*
            ) else (
                pwsh -ExecutionPolicy Bypass -File "007_sendto_zeitstempel_aus_dateinamen_entfernen.ps1" %*
            )
            

            Wo werden die Protokolle gespeichert?

            Bei den beiden Varianten für den aktuellen Ordner wird die Logdatei direkt im bearbeiteten Ordner gespeichert. Bei den beiden SendTo-Varianten liegt sie im Ordner des jeweiligen PowerShell-Skripts.

            Die Protokolldateien erhalten selbst einen Zeitstempel, zum Beispiel:

            umbenennprotokoll_zeitstempel_setzen_2026-08-21_14-40-05.log
            umbenennprotokoll_zeitstempel_entfernen_2026-08-21_14-42-17.log
            umbenennprotokoll_sendto_zeitstempel_setzen_2026-08-21_14-45-03.log
            umbenennprotokoll_sendto_zeitstempel_entfernen_2026-08-21_14-47-26.log

            Die Endung .log ist von der Verarbeitung ausgeschlossen. Dadurch wird auch eine bereits vorhandene Protokolldatei nicht als normale Zieldatei umbenannt.

            Typische Meldungen

            Datei würde umbenannt

            WÜRDE UMBENENNEN:
            Quelle: "C:\Daten\Rechnung.pdf"
            Ziel:   "C:\Daten\2026-08-21_14-35-42_Rechnung.pdf"
            ---

            Datei wurde umbenannt

            UMBENANNT:
            Quelle: "C:\Daten\2026-08-21_14-35-42_Rechnung.pdf"
            Ziel:   "C:\Daten\Rechnung.pdf"
            ---

            Zeitstempel ist bereits vorhanden

            ÜBERSPRUNGEN: "C:\Daten\2026-08-21_14-35-42_Rechnung.pdf" - Zeitstempel ist bereits vorhanden

            Kein Zeitstempel am Anfang gefunden

            ÜBERSPRUNGEN: "C:\Daten\Rechnung.pdf" - kein Zeitstempel am Anfang gefunden

            Dateiendung ist ausgeschlossen

            ÜBERSPRUNGEN: "C:\Daten\Hilfsskript.ps1" - Dateiendung ist ausgeschlossen

            Typische Fehler und mögliche Ursachen

            PowerShell 7 wurde nicht gefunden

            Die Starter-Batchdateien verwenden pwsh. Ist PowerShell 7 nicht installiert oder nicht über die Umgebungsvariable PATH erreichbar, kann das Skript nicht gestartet werden.

            Das PowerShell-Skript wird nicht gefunden

            Das PowerShell-Skript und seine passende Starter-Batchdatei müssen gemeinsam im vorgesehenen Ordner liegen. Auch die Dateinamen müssen unverändert zueinander passen.

            Es werden keine Dateien gefunden

            Die Skripte arbeiten nicht rekursiv. Dateien in tieferen Unterordnern werden absichtlich nicht erfasst.

            Eine Datei wird übersprungen

            Mögliche Gründe sind eine ausgeschlossene Dateiendung, ein bereits vorhandener Zeitstempel, ein fehlender Zeitstempel bei der Entfernen-Variante oder ein bereits vorhandener Zielname.

            Eine Datei kann nicht umbenannt werden

            Mögliche Ursachen sind fehlende Zugriffsrechte, eine in einem anderen Programm geöffnete Datei oder ein schreibgeschützter Datenträger.

            Eine SendTo-Auswahl wird doppelt übergeben

            Das Skript entfernt doppelte Pfade und führt zusätzlich eine Liste bereits erfasster Dateien. Eine Datei wird daher auch dann nur einmal geplant, wenn sie direkt und gleichzeitig über ihren Ordner in der Auswahl enthalten war.

            Wichtige PowerShell-Begriffe aus diesem Tutorial

            Get-Date -Format
            Erzeugt Datum und Uhrzeit in einem festgelegten Textformat.
            Get-Location
            Liefert den aktuellen Arbeitsordner. Die normalen Varianten nutzen diesen als zu bearbeitenden Ordner.
            $PSScriptRoot
            Liefert den Ordner des PowerShell-Skripts. Die SendTo-Varianten verwenden ihn als Speicherort für mögliche Logdateien.
            Get-ChildItem -File
            Ermittelt Dateien. Ohne -Recurse werden weitere Unterordner nicht durchsucht.
            -LiteralPath
            Behandelt einen Pfad wörtlich, sodass besondere Zeichen im Dateinamen nicht als Platzhalter interpretiert werden.
            -match und -notmatch
            Prüfen, ob ein Text zu einem regulären Ausdruck passt oder nicht passt.
            ^
            Markiert in einem regulären Ausdruck den Anfang eines Textes. Der Zeitstempel wird deshalb nur am Anfang des Dateinamens erkannt.
            \d{4}
            Steht für genau vier Ziffern. Entsprechend steht \d{2} für genau zwei Ziffern.
            ?
            Macht das unmittelbar davorstehende Zeichen im regulären Ausdruck optional. Hier betrifft das den Unterstrich nach der Sekundenangabe.
            [regex]::new
            Erstellt aus einem Muster ein Objekt für reguläre Ausdrücke.
            Replace
            Ersetzt einen gefundenen Text. Hier wird der erkannte Zeitstempel durch einen leeren Text ersetzt.
            [string]::IsNullOrEmpty
            Prüft, ob ein Text leer ist oder keinen Wert enthält.
            HashSet[string]
            Speichert eindeutige Texte. Hier werden damit bereits geplante Zielpfade reserviert.
            ToString("D3")
            Formatiert eine Zahl dreistellig mit führenden Nullen, zum Beispiel 001.
            Test-Path
            Prüft, ob eine Datei, ein Ordner oder ein geplanter Zielpfad bereits existiert.
            Join-Path
            Setzt einen Ordnerpfad und einen Dateinamen korrekt zusammen.
            [PSCustomObject]
            Erstellt ein übersichtliches Objekt mit selbst gewählten Eigenschaften. Hier speichert es die Daten einer geplanten Umbenennung.
            Rename-Item
            Benennt eine Datei oder einen Ordner um.
            ValueFromRemainingArguments
            Sammelt alle noch nicht zugeordneten Argumente des Aufrufs in einem Parameter.
            [string[]]
            Kennzeichnet ein Array, das mehrere Zeichenketten und damit mehrere übergebene Pfade aufnehmen kann.
            PSIsContainer
            Zeigt an, ob ein eingelesenes Element ein Ordner ist.
            ToLowerInvariant
            Erzeugt für die Dublettenprüfung eine kleingeschriebene, sprachunabhängige Vergleichsform des vollständigen Pfades.
            try und catch
            Fangen auftretende Fehler kontrolliert ab und ermöglichen verständliche Fehlermeldungen.
            Set-Clipboard
            Kopiert die gesammelte Ausgabe in die Windows-Zwischenablage.
            `n
            Erzeugt innerhalb eines PowerShell-Textes einen Zeilenumbruch.

            Wichtige Batch-Begriffe aus diesem Tutorial

            %~dp0
            Liefert Laufwerk und Ordner der aktuell ausgeführten Batchdatei.
            %~n0
            Liefert den Namen der Batchdatei ohne Pfad und Dateiendung.
            %*
            Enthält alle Argumente, die an die Batchdatei übergeben wurden.
            find /i
            Sucht ohne Beachtung der Groß- und Kleinschreibung nach einem Text. Hier wird damit _test im Namen der Starter-Batchdatei erkannt.
            errorlevel
            Enthält den Rückgabewert des zuletzt ausgeführten Programms oder Befehls.

            Was haben wir in diesem Teil gelernt?

            Mit Get-Date -Format "yyyy-MM-dd_HH-mm-ss" erzeugen wir einen gut sortierbaren Zeitstempel für Dateinamen.

            Ein regulärer Ausdruck erkennt, ob ein Zeitstempel bereits vorhanden ist. Die Entfernen-Varianten verwenden dasselbe Grundmuster, um den Präfix gezielt aus dem Basisnamen zu löschen.

            Die Funktion Get-FreierZielName schützt vorhandene Dateien und vergibt bei Bedarf Namen wie _001, _002 und so weiter. Zusätzlich verhindert ein HashSet[string] Konflikte zwischen mehreren geplanten Zielen.

            Die ersten beiden Varianten werden direkt in den zu bearbeitenden Ordner kopiert. Die beiden SendTo-Varianten übernehmen dagegen einzelne Dateien, Ordner oder gemischte Auswahlen als Argumente aus dem Windows-Explorer.

            Alle vier Skripte arbeiten nicht rekursiv, lassen bestimmte Skript- und Protokolldateien aus und unterstützen Testmodus, Sicherheitsabfrage, Logdatei und Zwischenablage.

            Damit haben wir für beide Arbeitsweisen einen vollständigen Ablauf: Zeitstempel hinzufügen, Ergebnis prüfen und den Zeitstempel bei Bedarf wieder sicher entfernen.

            Video zu diesem Tutorial:

             

            Das könnte Dich auch interessieren

            Keine Einträge gefunden

            Schreibe einen Kommentar Antwort abbrechen

            Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

            INTERESSANTE SEITEN

            • TUTORIALS SCHNELLER FINDEN
            • SCHNELLER RECHNEN LERNEN
            • MATHE AUFGABEN GENERATOR
            • LINIEN UND KARO GENERATOR
            • VORLAGEN ZUM AUSDRUCKEN
            • BINGO-SCHEIN-GENERATOR
            • MEINE WUNSCHLISTE
            • Symbole zum Kopieren

            Content

            • Microsoft Excel Tutorials
            • Microsoft Excel VBA Tutorials
            • Microsoft Word Tutorials
            • Microsoft Powerpoint Tutorials
            • Microsoft Windows 10 Tutorials
            • Google Tabellen Tutorials
            • Google Tabellen Script Tutorials
            • Google Docs Tutorials
            • LibreOffice Calc Tutorials
            • Libre Office Writer Tutorials
            • LibreOffice Impress Tutorials
            • OpenOffice Calc Tutorials
            • OpenOffice Writer Tutorials
            • OpenOffice Impress Tutorials
            • WPS Spreadsheets Tutorials
            • WPS Spreadsheets Tutorials

            Neue Beiträge

            • Python Grundkurs Teil 007: Zeitstempel hinzufügen und entfernen
            • PowerShell 7 Grundkurs Teil 007: Zeitstempel hinzufügen und entfernen
            • Batch Grundkurs 007: Zeitstempel hinzufügen und entfernen
            • Python Grundkurs Teil 006: Ordnername als Präfix
            • PowerShell 7 Grundkurs Teil 006: Ordnername als Präfix

            Neue Kommentare

            • Chris zu Tutorial Wunsch Liste
            • Rupert zu Tutorial Wunsch Liste
            • Axel zu Datumsformat anpassen (LibreOffice Calc)
            • Klaus zu Dynamischen Monatskalender erstellen + Download (LibreOffice Calc)
            • ROBERT SCHMIDT zu MsgBox in Excel VBA
            • Chris zu Python in LibreOffice Calc: Formular auslesen und auswerten

            Schlagwörter-Wolke

            anfänger Anleitung anpassen ausrechnen auswahlliste Bedingte Formatierung berechnen Calc Datum Download Dropdownliste einfügen entfernen erstellen Excel FreeOffice Funktion google hinzufügen Jahresplan Kalender Lernvideo LibreOffice löschen Makro markieren Microsoft OpenOffice PlanMaker programmierung script spreadsheets Sverweis Tabelle tabellen Tutorial VBA Video Wenn Wenn Funktion Word wps spreadsheets Zeilen Zelle Zellen
            KONTAKT   IMPRESSUM    DATENSCHUTZERKLÄRUNG    HAFTUNGSAUSSCHLUSS (Disclaimer)   © copyright TOPTORIALS