Public/New-sqmRobocopyCommand.ps1

function New-sqmRobocopyCommand {
    <#
    .SYNOPSIS
        Erzeugt einen robocopy-Aufruf als Kommandozeile, Batch-Datei, PowerShell-Skript oder
        Skript fuer eine geplante Aufgabe.
    .DESCRIPTION
        Nur die in -Parameter gesetzten Optionen landen im Befehl, in fester Reihenfolge (Quelle,
        Ziel, Dateien, Optionen wie in der GUI). Verzeichnisse werden ohne abschliessenden Backslash
        ausgegeben - "C:\Ziel\" in Anfuehrungszeichen ist der haeufigste robocopy-Fehler.
    .PARAMETER Parameter
        Hashtable mit Source, Destination, Files (Array oder ;-getrennt) und den robocopy-Optionen
        ohne Schraegstrich: @{ E = $true; R = 3; XF = '*.tmp;Thumbs.db'; MT = 16; LOG = 'C:\x.log' }.
        Optionen mit optionalem Wert (MT, SPARSE, LFSM) als $true (ohne Wert) oder mit Wert.
    .PARAMETER Format
        CommandLine (Standard), Batch (.cmd mit Exitcode-Auswertung), PowerShell, ScheduledTask
        (Register-ScheduledTask-Skript) oder Arguments (rohe Argumentliste als string[]).
    .EXAMPLE
        New-sqmRobocopyCommand -Parameter @{ Source = 'D:\Daten'; Destination = '\\nas\backup\Daten'; MIR = $true; R = 3; W = 5; MT = 16 }
    .EXAMPLE
        New-sqmRobocopyCommand -Parameter $p -Format ScheduledTask -ScheduleType Daily -ScheduleTime 22:00
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][hashtable]$Parameter,
        [ValidateSet('CommandLine', 'Batch', 'PowerShell', 'ScheduledTask', 'Arguments')][string]$Format = 'CommandLine',
        [string]$TaskName,
        [string]$TaskPath = '\sqmRobocopy\',
        [string]$TaskUser = 'SYSTEM',
        [ValidateSet('None', 'Once', 'Daily', 'Weekly', 'Interval')][string]$ScheduleType = 'Daily',
        [string]$ScheduleTime = '22:00',
        [ValidateSet('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday')][string[]]$ScheduleWeekDays = @('Sunday'),
        [ValidateRange(1, 1440)][int]$IntervalMinutes = 60
    )

    $unknown = @($Parameter.Keys | Where-Object { $_ -notin 'Source', 'Destination', 'Files' -and -not (Get-srgParameterLookup).ContainsKey($_) })
    if ($unknown.Count) { throw "Unbekannte robocopy-Option(en): $($unknown -join ', ')" }

    $args_ = Get-srgArgumentList -Parameter $Parameter
    # bewusst ohne Komma-Operator: '& robocopy.exe @(New-sqmRobocopyCommand ... -Format Arguments)' braucht eine flache Liste
    if ($Format -eq 'Arguments') { return $args_ }

    $src = [string]$Parameter['Source']
    $dst = [string]$Parameter['Destination']
    $head = "sqmRobocopyGui $script:srgModuleVersion - $(Get-Date -Format 'yyyy-MM-dd HH:mm')"

    switch ($Format) {
        'CommandLine' { return 'robocopy ' + (Join-srgCmdLine $args_) }

        'Batch' {
            $lines = @(
                '@echo off'
                ":: robocopy - erzeugt mit $head"
                ':: Exitcode 0-7 = Erfolg (Bitmaske: 1 kopiert, 2 Extra, 4 Mismatch), ab 8 = Fehler'
                'chcp 65001 >nul'
                ''
                ('robocopy ' + (Join-srgCmdLine $args_ -Batch))
                'set "RC=%ERRORLEVEL%"'
                ''
                'if %RC% GEQ 8 ('
                ' echo robocopy FEHLER - Exitcode %RC%'
                ' exit /b %RC%'
                ')'
                'echo robocopy OK - Exitcode %RC%'
                'exit /b 0'
            )
            return ($lines -join "`r`n")
        }

        'PowerShell' {
            $sb = New-Object System.Text.StringBuilder
            [void]$sb.AppendLine("# robocopy - erzeugt mit $head")
            [void]$sb.AppendLine('# Exitcode 0-7 = Erfolg (Bitmaske: 1 kopiert, 2 Extra, 4 Mismatch), ab 8 = Fehler')
            [void]$sb.AppendLine('$robocopyArgs = @(')
            foreach ($a in $args_) { [void]$sb.AppendLine(" $(Format-srgPsArg $a)") }
            [void]$sb.AppendLine(')')
            [void]$sb.AppendLine('')
            [void]$sb.AppendLine('& robocopy.exe @robocopyArgs')
            [void]$sb.AppendLine('$rc = $LASTEXITCODE')
            [void]$sb.AppendLine('if ($rc -ge 8) { throw "robocopy fehlgeschlagen - Exitcode $rc" }')
            [void]$sb.Append('Write-Host "robocopy OK - Exitcode $rc"')
            return $sb.ToString()
        }

        'ScheduledTask' {
            if (-not $TaskName) {
                $leaf = { param($p) if ($p) { (ConvertTo-srgDirectory $p).TrimEnd('\').Split('\')[-1] } else { '?' } }
                $TaskName = "Robocopy $(& $leaf $src) nach $(& $leaf $dst)"
            }
            if (-not $TaskPath.StartsWith('\')) { $TaskPath = '\' + $TaskPath }
            if (-not $TaskPath.EndsWith('\')) { $TaskPath += '\' }
            if ($ScheduleTime -notmatch '^([01]?\d|2[0-3]):[0-5]\d$') { throw "Ungueltige Uhrzeit '$ScheduleTime' (erwartet HH:mm)." }
            $q = { param($s) "'" + "$s".Replace("'", "''") + "'" }

            $trigger = switch ($ScheduleType) {
                'None' { $null }
                'Once' { "New-ScheduledTaskTrigger -Once -At $(& $q $ScheduleTime)" }
                'Daily' { "New-ScheduledTaskTrigger -Daily -At $(& $q $ScheduleTime)" }
                'Weekly' { "New-ScheduledTaskTrigger -Weekly -DaysOfWeek $($ScheduleWeekDays -join ', ') -At $(& $q $ScheduleTime)" }
                'Interval' { "New-ScheduledTaskTrigger -Once -At $(& $q $ScheduleTime) -RepetitionInterval (New-TimeSpan -Minutes $IntervalMinutes)" }
            }
            $principal = if ($TaskUser -eq 'SYSTEM') {
                "New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType ServiceAccount -RunLevel Highest"
            } else {
                "New-ScheduledTaskPrincipal -UserId $(& $q $TaskUser) -LogonType Interactive -RunLevel Limited"
            }
            $desc = "robocopy $(ConvertTo-srgDirectory $src) -> $(ConvertTo-srgDirectory $dst) (sqmRobocopyGui $script:srgModuleVersion)"

            $lines = @(
                "# Geplante Aufgabe fuer robocopy - erzeugt mit $head"
                '# Als Administrator ausfuehren. Ergebnis 0x0-0x7 in der Aufgabenplanung = Erfolg, ab 0x8 = Fehler.'
                $(if ($TaskUser -eq 'SYSTEM') { '# Laeuft als SYSTEM: Netzwerkfreigaben werden mit dem Computerkonto (DOMAENE\RECHNER$) angesprochen.' }
                    else { "# Laeuft als $TaskUser - nur solange der Benutzer angemeldet ist." })
                ''
                "`$taskName = $(& $q $TaskName)"
                "`$taskPath = $(& $q $TaskPath)"
                "`$arguments = $(& $q (Join-srgCmdLine $args_))"
                ''
                'if (Get-ScheduledTask -TaskName $taskName -TaskPath $taskPath -ErrorAction SilentlyContinue) {'
                ' throw "Aufgabe $taskPath$taskName existiert bereits - sie wird nicht ueberschrieben."'
                '}'
                '$action = New-ScheduledTaskAction -Execute "$env:SystemRoot\System32\Robocopy.exe" -Argument $arguments'
                $(if ($trigger) { "`$trigger = $trigger" })
                "`$principal = $principal"
                '$settings = New-ScheduledTaskSettingsSet -StartWhenAvailable -MultipleInstances IgnoreNew -ExecutionTimeLimit (New-TimeSpan -Hours 72)'
                $(if ($trigger) {
                        "Register-ScheduledTask -TaskName `$taskName -TaskPath `$taskPath -Action `$action -Trigger `$trigger -Principal `$principal -Settings `$settings -Description $(& $q $desc) | Out-Null"
                    } else {
                        "Register-ScheduledTask -TaskName `$taskName -TaskPath `$taskPath -Action `$action -Principal `$principal -Settings `$settings -Description $(& $q $desc) | Out-Null"
                    })
                'Write-Host "Aufgabe $taskPath$taskName angelegt."'
            )
            return (@($lines | Where-Object { $null -ne $_ }) -join "`r`n")
        }
    }
}