diemilz
Goto Top

PowerShell-Fehlermeldung beim Parsen

Hallo zusammen,

da ich noch nicht so sonderlich versiert in PowerShell bin, bitte ich um Nachsicht, wenn ich hier den Wald vor lauter Bäumen nicht sehen sollte.

Ich habe den Auftrag erhalten, ein bereits bestehendes PowerShell-Skript zum Erstellen von SQL-Backups auf unseren Kassensystemen in unseren Außenlokationen anzupassen. Das Skript funktioniert auch soweit, nur bei einer Funktion habe ich ein Problem. Ich poste erstmal den Code der Funktion. Leider darf ich nicht das ganze Skript posten:

Function Backup-Markt { 
    param ( [Parameter(Mandatory=$true)][string]$Source,
	[Parameter(Mandatory=$true)][string]$Destination,
    [Parameter(Mandatory=$false)][string]$FileSpec = "*.*",  
    [Parameter(Mandatory=$false)][string]$LoggingPath = $Env:Temp,
    [Parameter(Mandatory=$false)][string]$LogfileName = "robocopy.log",  
    [switch]$Help)
 
    $HelpInfo = @'  
 
    Function : Backup-Market
    Date     : 01/13/2015
    Purpose  : Performs a backup of folder named $Source to $Destination
    Usage    : $status = Backup-Market -Source <Path> - Destination <Path> -sourceCredentialsFile <File> -destinationCredentialsFile <File> [-Help]
               where
                  $status       will contain a return object with robocopy information,
                                $status.ExitCode:
                                -1 = source or destination path does not exist / not accessible
                                 0 = No Errors and no Copies
                                 1 = No Errors and new Files Copied
                                 2 = Warning! Extra Files in Dest. Folder
                                 4 = Mismatching Files
                                 8 = Errors on Copying
                                16 = Error no Copy
                                $status.StartTime, $status.Source, $status.Destination, $status.FileName, $status.Options
                  -Source       source folder to backup
                  -Destination  folder for backing up, will be created if it does not exist
                  -FileSpec     filename specification for files to copy, ex. *.bak
                  -LoggingPath  directory to place logfiles in
                  -LogfileName  filename for logfile, ex. $ID.log
                  -Help         displays this help information
 
'@  

	$RoboStatus = New-Object object
    $enCulture = [Globalization.cultureinfo]::GetCultureInfo("en-US")  

	# Create the Backup Destination Folder Based on Date Information
	If (!(Test-Path $Destination -pathType container)) { 
        Try {
            $x = mkdir $Destination -ErrorAction Stop
        }
        Catch {
            $ExitCode = -1
        }
    }
	
	# Testing - Available Return Codes from Robocopy to use in Control the Script Flow using Error Codes
	$RoboCopyExitCodes = @{0 = "No Errors, no copying"; 1 = "No Errors, new Files"; 2 = "Warning, extra  
	Files"; 4 = "Warning, mismatching Files"; 8 = "Errors, copy errors"; 16 = "ERROR, no copy"}  
	
	$date = Get-Date -Format yyyy.MM.dd-HH.mm
	$Logfile = "$LoggingPath\$Logfilename"  

	# Copy/Move Folder/Files with Robocopy and Create Logfile + Put Log to a Variable {{comment_single_line_double_slash:0}}
	# You can fit here the Robocopy Parameters as you want.
    If ((Test-Path $Source -PathType container) -and (Test-Path $Destination -PathType container)) {

        $robolog = Robocopy """$Source""" """$Destination""" """$FileSpec""" /R:3 /W:100 /NP /TEE /MAXAGE:1 /log:$Logfile 2>&1  
        Write-Host $robolog[0..$robolog.length]

        # Get the Robocopy Exit Code for later Identifying Job Status
        $ExitCode = $LastExitCode
		
		Add-Member -InputObject $RoboStatus -Name ExitCode -Value $ExitCode -MemberType NoteProperty
		
		If ((Get-WmiObject Win32_OperatingSystem).OSlanguage = "1031") {  
			# Begin of Parsing Log File / German
			$robolog[0..$robolog.length] | % {
			Switch -regex ($_) {
			**'Gestartet: (.*)' {  
				Add-Member -InputObject $RoboStatus -Name StartTime -Value ($matches[1].trim()) -MemberType NoteProperty
                [datetime]$StartTime = [datetime]::ParseExact($matches[1].trim(), "ddd MMM dd HH:mm:ss yyyy", $enCulture)  
			}
			'Beendet: (.*)' {  
				Add-Member -InputObject $RoboStatus -Name EndTime -Value ($matches[1].trim()) -MemberType NoteProperty
                [datetime]$EndTime = [datetime]::ParseExact($matches[1].trim(), "ddd MMM dd HH:mm:ss yyyy", $enCulture)  
                Add-Member -InputObject $RoboStatus -Name Duration -Value $(New-Timespan –Start $StartTimeEnd $EndTime) -MemberType NoteProperty
			}**
			'Quelle : (.*)' {  
				Add-Member -InputObject $RoboStatus -Name Source -Value ($matches[1].trim()) -MemberType NoteProperty
			}
			'Ziel : (.*)' {  
				Add-Member -InputObject $RoboStatus -Name Destination -Value ($matches[1].trim()) -MemberType NoteProperty
			}
			'Dateien : (.*)' {  
				Add-Member -InputObject $RoboStatus -Name FileName -Value ($matches[1].trim()) -MemberType NoteProperty
			}
			'Optionen: (.*)' {  
				Add-Member -InputObject $RoboStatus -Name Options -Value ($matches[1].trim()) -MemberType NoteProperty
			}
            'Dateien:([\s]*)([0-9]+)([\s]*)([0-9]+)([\s]*)([0-9]+)([\s]*)([0-9]+)([\s]*)([0-9]+)([\s]*)([0-9]+)' {  
                #Total Copied Skipped Mismatch FAILED Extras
				Add-Member -InputObject $RoboStatus -Name NumberOfFilesTotal -Value ($matches[2].trim()) -MemberType NoteProperty
				Add-Member -InputObject $RoboStatus -Name NumberOfFilesCopied -Value ($matches[4].trim()) -MemberType NoteProperty
				Add-Member -InputObject $RoboStatus -Name NumberOfFilesSkipped -Value ($matches[6].trim()) -MemberType NoteProperty
				Add-Member -InputObject $RoboStatus -Name NumberOfFilesMismatch -Value ($matches[8].trim()) -MemberType NoteProperty
				Add-Member -InputObject $RoboStatus -Name NumberOfFilesFailed -Value ($matches[10].trim()) -MemberType NoteProperty
				Add-Member -InputObject $RoboStatus -Name NumberOfFilesExtras -Value ($matches[12].trim()) -MemberType NoteProperty
			}             
            'Verzeich.:([\s]*)([0-9]+)([\s]*)([0-9]+)([\s]*)([0-9]+)([\s]*)([0-9]+)([\s]*)([0-9]+)([\s]*)([0-9]+)' {  
                #Total Copied Skipped Mismatch FAILED Extras
				Add-Member -InputObject $RoboStatus -Name NumberOfFoldersTotal -Value ($matches[2].trim()) -MemberType NoteProperty
				Add-Member -InputObject $RoboStatus -Name NumberOfFoldersCopied -Value ($matches[4].trim()) -MemberType NoteProperty
				Add-Member -InputObject $RoboStatus -Name NumberOfFoldersSkipped -Value ($matches[6].trim()) -MemberType NoteProperty
				Add-Member -InputObject $RoboStatus -Name NumberOfFoldersMismatch -Value ($matches[8].trim()) -MemberType NoteProperty
				Add-Member -InputObject $RoboStatus -Name NumberOfFoldersFailed -Value ($matches[10].trim()) -MemberType NoteProperty
				Add-Member -InputObject $RoboStatus -Name NumberOfFoldersExtras -Value ($matches[12].trim()) -MemberType NoteProperty
			} } }   # End of Parsing Log File
		} Else {
			# Begin of Parsing Log File / English
			$robolog[$StartBegin..$StartEnd] | % {
			Switch -regex ($_) {
			'Started :(.*)' {  
			Add-Member -InputObject $RoboStatus -Name StartTime -Value ($matches[1].trim()) -MemberType NoteProperty
			}
			'Source :(.*)' {  
			Add-Member -InputObject $RoboStatus -Name Source -Value ($matches[1].trim()) -MemberType NoteProperty
			}
			'Dest :(.*)' {  
			Add-Member -InputObject $RoboStatus -Name Destination -Value ($matches[1].trim()) -MemberType NoteProperty
			}
			'Files :(.*)' {  
			Add-Member -InputObject $RoboStatus -Name FileName -Value ($matches[1].trim()) -MemberType NoteProperty
			}
			'Options :(.*)' {  
			Add-Member -InputObject $RoboStatus -Name Options -Value ($matches[1].trim()) -MemberType NoteProperty
			} }	}   # End of Parsing Log File
		}
    } Else {
        $ExitCode = -1
        Add-Member -InputObject $RoboStatus -Name ExitCode -Value $ExitCode -MemberType NoteProperty
    }
	
    Return $RoboStatus
}

Die betroffenen Zeilen habe ich fett markiert. Hier wird folgender Fehler ausgespuckt:

Ausnahme beim Aufrufen von "ParseExact" mit 3 Argument(en):  "Die Zeichenfolge wurde nicht als gültiges DateTime erkannt."  
In D:\Backup\BackupScript.ps1:178 Zeichen:17
+ ...             [datetime]$StartTime = [datetime]::ParseExact($matches[1] ...
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) , MethodInvocationException
    + FullyQualifiedErrorId : FormatException

Ausnahme beim Aufrufen von "ParseExact" mit 3 Argument(en):  "Die Zeichenfolge wurde nicht als gültiges DateTime erkannt."  
In D:\drink.PoS.Pro.Backup\BackupScript.ps1:182 Zeichen:17
+ ...             [datetime]$EndTime = [datetime]::ParseExact($matches[1].t ...
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) , MethodInvocationException
    + FullyQualifiedErrorId : FormatException

Die Variable $robolog hat den folgenden Inhalt:

  Protokolldatei: D:\Backup\Logs\10002_robocopy.log  -------------------------------------------------------------------------
------    ROBOCOPY     ::     Robustes Dateikopieren für Windows                               ---------------------------------------------
----------------------------------    Gestartet: Donnerstag, 11. Oktober 2018 16:39:59    Quelle : \\test1\backup\      Ziel : \\NA
S01\Backup\10002\2018.10.11\      Dateien : *.7z         Optionen: /TEE /DCOPY:DA /COPY:DAT /NP /MAXAGE:1 /R:3 /W:100   --------------
----------------------------------------------------------------                           1    \\test1\backup\             Neuer
  610643        dbbak.7z  ------------------------------------------------------------------------------             Insgesamt   KopiertÜber
sprungenKeine Übereinstimmung    FEHLER    Extras Verzeich.:         1         0         0         0         0         0   Dateien:
1         1         0         0         0         0     Bytes:   596.3 k   596.3 k         0         0         0         0    Zeiten:   0:00
:00   0:00:00                       0:00:00   0:00:00   Geschwindigkeit:             7828756 Bytes/Sek. Geschwindigkeit:             447.965
 Megabytes/Min.    Beendet: Donnerstag, 11. Oktober 2018 16:39:59

Das Skript generiert eine E-Mail, wo u.a. auch aufgeführt werden soll, wie lange der Kopiervorgang gedauert hat. Das soll durch die Subtraktion der Startzeit von der Endzeit passieren, welche beide aus der Variable rausgezogen werden sollen. Im alten Skript ist der Block 1:1 auch so vorhanden und funktioniert einwandfrei.

Hat jemand eine Idee?

VIelen Dank schonmal im Voraus.

LG Stephan

Content-ID: 389172

Url: https://administrator.de/contentid/389172

Ausgedruckt am: 24.11.2024 um 23:11 Uhr

137443
137443 11.10.2018 aktualisiert um 17:18:25 Uhr
Goto Top
'Gestartet: (.*)'
Das ist dein Regex der das Datum capturen soll, fällt dir da nichts auf?
Genau du erfasst zu viel für das Datum (deswegen meckert auch die ParseExact Funktion weil da eben Daten hinter dem Datum sind die es nicht interpretieren kann, dein Log zeigt es dir ja das hinter dem Datum noch ohne Zeilenumbruch kommt:
    Quelle : \\test1\backup\      Ziel : \\NA

, also Regex zuverlässig anpassen das er nur auf Datumswerte passt, btw. das Startdatum braucht man ja eigentlich nicht auslesen das kennt man ja du startest es ja selbst ein get-date hätte also gereicht face-smile.

Gruß l
diemilz
diemilz 11.10.2018 um 19:23:16 Uhr
Goto Top
Was mich wundert, dass es in der selben Konstellation in der alten Umgebung noch funktioniert hat und die alte Umgebung läuft auf dem gleichen Server. Das Skript habe ich nicht geschrieben, es stammt von unserem alten IT Dienstleister, der allerdings keinerlei Doku darüber erstellt hat. Ist aber ne andere Geschichte.

Mit dem get-date hast du mich aber auf eine interessante Spur gebracht. Ich teste das morgen mal aus. Idealerweise wäre es halt, Start- und Endzeit aus der Robocopy-Ausgabe für die Berechnung zu nehmen, so wie es im alten Konstrukt auch läuft.

Ich melde mich, sobald ich es getestet habe. Vielen Dank schonmal für den Tipp.
diemilz
diemilz 12.10.2018 um 08:28:13 Uhr
Goto Top
Ich habe jetzt mal versucht, das mit get-date umzusetzen. Grundsätzlich funktioniert das auch, aber die Ausgabe ist total vermurkst. Das Ganze sieht jetzt wie folgt aus:

$robostart = Get-Date
robocopy ..........
$roboend = Get-Date
$robospantmp = New-Timespan -Start $robostart -End $roboend

$robospan = "($robospantmp.Hours):($robospantmp.Minutes):($robospantmp.Seconds)"  

Die Ausgabe von New-Timespan ist diese:

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 11
Milliseconds      : 899
Ticks             : 118990535
TotalDays         : 0,00013772052662037
TotalHours        : 0,00330529263888889
TotalMinutes      : 0,198317558333333
TotalSeconds      : 11,8990535
TotalMilliseconds : 11899,0535

Die Ausgabe der Variable $robospan ist aber diese hier:

(00:00:11.8990535.Hours):(00:00:11.8990535.Minutes):(00:00:11.8990535.Seconds)

Das scheint PowerShell gar nicht wirklich zu interessieren, welche Werte ich abfrage. Oder habe ich wieder einen fundamentalen Denkfehler drin?
137443
137443 12.10.2018 um 09:11:45 Uhr
Goto Top
Oder habe ich wieder einen fundamentalen Denkfehler drin?
Jupp.
diemilz
diemilz 17.10.2018 aktualisiert um 09:03:05 Uhr
Goto Top
Ok. Hab den Fehler gefunden, bei $robospan habe ich die Dollarzeichen im Aufbau der Variable vergessen. Jetzt bekomme ich die korrekte Ausgabe angezeigt.

Jetzt habe ich ein weiteres Problem:
Das gleiche Skript liest aus einer XML-Datei aus, welche Remote-Lokationen es gibt und welche Kassen dort vorhanden sind. Die Datei sieht wie folgt aus:

<?xml version="1.0" encoding="utf-8"?>  

<BackupConfig>
  <Markt ID="10409">  
    <description>Lokation I</description>
	<pos>M</pos>
	<pos>S</pos>
	<fileSpec>*.7z</fileSpec>
  </Markt>
  <Markt ID="10410">  
    <description>Lokation II</description>
	<pos>M</pos>
	<pos>S</pos>
	<fileSpec>*.7z</fileSpec>
  </Markt>
  <Markt ID="10411">  
    <description>Lokation III</description>
	<pos>M</pos>
	<pos>S</pos>
	<fileSpec>*.7z</fileSpec>
  </Markt>
</BackupConfig>

Das PowerShell-Skript soll jetzt jeden Markt (<Markt ID> durchgehen und darin jede Kasse <pos> sichern. Dazu habe ich zwei foreach-Loops gebaut:

foreach($markt in $xmlfile.BackupConfig.Markt) {
        Write-Host ("[{0}] {1} - {2}" -f $(Get-Date -Format u), $markt.ID, $markt.description) -ForegroundColor Yellow   
        $result_lines[$markt.ID] = @{}
        $result_lines[$markt.ID]["description"] = $markt.description  
        foreach($postype in $xmlfile.BackupConfig.Markt.pos) {
                $pos = $($markt.ID+"-"+$postype)  
                $posbackuppath = $("\\"+$pos+"\Backup")  
                $result_lines[$markt.ID]["pos"] = $markt.BackupConfig.Markt.pos  
                 If (!(Test-Connection -ComputerName $pos -Count 2 -Quiet)) {
                        Write-Host "PoS"$pos" not available. Proceeding to next PoS or location."  
                        continue
                 }
                do stuff
        }
}

Das Skript tut nicht ganz, was es soll. Es geht jede Markt ID durch und liest dort jede Kasse aus, aber immer über die gesamte XML-Datei. Im obigen Beispiel habe ich zwei Kassen pro Markt, drei Märkte mit insgesamt sechs Kassen. Ich bekomme nun aber pro Markt alle sechs Kassen abgearbeitet. Wie muss ich den zweiten foreach-Loop setzen, damit ich nur die Kassen pro Markt abgearbeitet bekomme?

Im Prinzip möchte ich einfach nur jeden pos-Eintrag pro Markt bearbeitet haben, nicht pro Markt jeden pos-Eintrag der Datei.