none
Fortschreibende Export-CSV erstellen mit PS V1.0 RRS feed

  • Frage

  • Hallo zusammen,

    ich bin neu in dem Thema Powershell Scripting und brauche Hilfe bei folgender Aufgabe:

    Ich möchte aus mehreren Ordnern, Logfiles (im XML Format) mit einem bestimmten Muster herausfiltern und zusammengefügt als CSV-Datei wieder ausgeben. Dabei möchte ich zum einen eine Export-Datei die mir alle Errors zeigt und zum anderen eine Export-Datei die mir jeden Fehler nur einmal auflistet. Soweit hat das funktioniert. jetzt stellt sich mir die Frage wie mit Hilfe von Export-CSV eine fortschreibende Datei erzeugen kann, wenn das Script mehrfach in der Woche gestartet wird. Mit PS V3.0 geht das mit -Append, ich verwende allerdings PS V1.0.

    Kann mir jemand weiterhelfen?

    Hier mal mein Script:

    #LogScript für AppServer
    
    Write-host "Skript zur Konsolidierung der Logsfiles auf dem AppServer (Abnahme und Produktion)"
    
    $DebugPreference = "Continue"
    
    
    #Suche nach folgenden Mustern in der Dateibeschreibung
    
    $pattern2 = "QServer"
    $pattern3= "QDBODBC"
    
    #Pfad des Lofile Ordners:
    $path = "D:\Users\...\LogfilesApp10.05.2016"
    
    #Suche Inhalt des Pfades
    
    $files = ls $path | Where-Object {$_.Extension -eq '.xml'}
    $files.gettype();
    
     
    foreach ($file in $files) {
    #Abfrage der Muster
     
      
    
        If($file -match $pattern2)
            {
            [xml] $xmldoc = Get-Content $file.FullName
            $finalCSV_QServer += $xmldoc.root.logentries.logentry | Where {$_.logkind -Match "Error" -or $_.logkind -Match "Warning" -or $_.logkind -Match "Caution" } 
            }
    
    
          If($file -match $pattern3)
            {
            [xml] $xmldoc = Get-Content $file.FullName
            $finalQDBODBC += $xmldoc.root.logentries.logentry | Where {$_.logkind -Match "Error" -or $_.logkind -Match "Warning" -or $_.logkind -Match "Caution" } 
            }
    
    }  
    #Wie bekommt man hier eine fortschreibende Ausgabe in eine Datei mit Powershell V1?
    
    $finalCSV_QServer | Export-CSV "$path\PS-Test2.csv" -NoType -Delimiter ";" -Encoding UTF8 #<------
    $finalQDBODBC| Export-CSV "$path\PS-Test3.csv" -NoType -Delimiter ";" -Encoding UTF8 #<-----
    
    
    
    
    #Wie bekommt man hier eine fortschreibende Ausgabe in eine Datei mit Powershell V1?
    
    
    $Cons1 = ($finalCSV_QServer| sort description -unique)
    $cons1| Export-CSV "$path\PS-TestCons2.csv" -NoType -Delimiter ";" -Encoding UTF8 #<------
    
    
    
    $Cons2 = ($finalQDBODBC| sort description -unique)
    $cons2| Export-CSV "$path\PS-TestCons3.csv" -NoType -Delimiter ";" -Encoding UTF8 #<------
    
    
    remove-variable -Name finalCSV_QServer
    remove-variable -Name finalQDBODBC
    
    

    Mittwoch, 11. Mai 2016 09:54

Antworten

  • Hallo,

    ein StreamReader liest Dateien ein, keine Arrays oder anderen Variablen. Deshalb musst du einen Dateipfad an den Streamreader übergeben. 

    Ich habe den Code selbst schnell zusammengeschrieben:

    $dir = Split-Path $MyInvocation.MyCommand.Path
    
    #Pfad der zu ergänzenden Datei
    $CSVDateiPfad = "$dir\Daten.csv"
    
    #die hinzuzufügenden Daten (hier kommt deine Abfrage hin)
    $NeueDaten = Import-Csv "$dir\NeueDaten.csv" -Delimiter ";"
    
    #StreamReader öffnen und Tabellenkopf lesen
    $Reader = New-Object System.IO.StreamReader($CSVDateiPfad) 
    $Header = $Reader.ReadLine()
    $AusgabeFelder = $Header -split ";"
    
    #StreamReader wieder schließen
    $Reader.Close()
    
    #StreamWriter öffnen
    $Writer = New-Object System.IO.StreamWriter($CSVDateiPfad, $true)
    
    #Daten zeilenweise anfügen
    foreach($d in $NeueDaten)
    {
        #Ausgabezeile formen; Reihenfolge durch $AusgabeFelder sichergestellt
        $Zeile = ($AusgabeFelder | foreach{$d.$_}) -join ";"
        $Writer.WriteLine($Zeile)
    }
    
    $Writer.Close()

    In $csvDateiPfad muss der Pfad der AusgabeDatei stehen. $NeueDaten muss deine neu generierten Daten enthalten; als Beispiel habe ich eine csv-Datei importiert.

    Durch das $true wird der StreamWriter in einen Anfügemodus versetzt, sodass er die bisherige Datei nicht überschreibt.

    Dieser Code, den du geschrieben hast:

    ($header | foreach{$o.$Cons1}) -join ";"

    ist völlig sinnlos. Das meine ich nicht bösartig, aber du solltest dich unbedingt mehr mit Programmierung beschäftigen, wenn du derartige Projekte umsetzen willst. $Cons1 ist ein Array von Objekten. $o wäre, wie ich es oben definiert hatte, ein einzelner Datensatz aus diesem Array. Du kannst hinter dem Punkt nur eine Eigenschaften-Bezeichnung stehen haben oder eine Variable, die eine Eigenschaftenbezeichnung enthält. Nie ein Array!! Außerdem muss in einer foreach-Objekt-Schleife immer der Operator "$_" vorkommen, sonst ist sie per se falsch, da du ja das jeweilige Schleifenobjekt überhaupt nicht aufrufst.

    Viele Grüße

    Christoph

    • Als Antwort vorgeschlagen Tschenn1 Dienstag, 24. Mai 2016 12:10
    • Nicht als Antwort vorgeschlagen Tschenn1 Dienstag, 24. Mai 2016 12:10
    • Als Antwort vorgeschlagen hpotsirhc Donnerstag, 26. Mai 2016 11:52
    • Als Antwort markiert Denniver ReiningMVP, Moderator Samstag, 28. Mai 2016 21:43
    Montag, 23. Mai 2016 09:04

Alle Antworten

  • Get-Process | Export-Csv -Path D:\pro.csv #Daten erzeugen
    
    $old = Import-Csv -Path D:\pro.csv #Daten in csv exportieren
    
    $new = Get-Process #neue Daten werden erzeugt
    
    $old + $new | Export-Csv -Path D:\pro.csv -NoTypeInformation #alte und neue Daten zusammen unter dem alten pfad wegspeichern
    Gruß,
    Mittwoch, 11. Mai 2016 11:30
  • Hallo,

    ich weiß nicht, ob das schon in V1 vorhanden war, aber du kannst auch mit ConvertTo-Csv ein String-Array erstellen, dass in der Struktur einer CSV-Datei entspricht. Es wird genauso verwendet wie Export-Csv, nur halt dass es in eine Variable schreibt und nicht in eine Datei.

    Diesen Inhalt kannst du mit Add-Content an eine bestehende Datei anhängen. Du musst nur irgendwie sicherstellen, dass die Feldreihenfolge identisch ist. Dafür kannst du z.B. mit einem System.IO.StreamReader nur die erste Zeile der bestehenden Csv lesen. Ich würde auf jeden Fall davon abraten, die gesamte alte CSV mit Import-CSV zu lesen. Wenn die csv groß ist, frisst das viel Speicher und ist einfach unperformant.

    Was ich mich aber noch mehr frage: Warum benutzt du PowerShell V1.0? V2.0 kann man selbst auf Windows XP installieren.

    Viele Grüße

    Christoph

    Mittwoch, 11. Mai 2016 11:37
  • Vielen Dank für die schnelle Antwort. Bezogen auf mein Beispiel müsste das meiner Meinung nach so aussehen(s.u.)

    $Cons1old = Import-CSV "$path\PS-TestCons2.csv" -NoType -Delimiter ";" -Encoding UTF8 
    $Cons1 = ($finalCSV_QServer| sort description -unique)
    $Cons1 + $Cons1old| Export-CSV "$path\PS-TestCons2.csv" -NoType -Delimiter ";" -Encoding UTF8
    
    $Cons2old = Import-CSV "$path\PS-TestCons3.csv" -NoType -Delimiter ";" -Encoding UTF8 
    $Cons2 = ($finalQDBODBC| sort description -unique)
    $Cons2 + $cons2old| Export-CSV "$path\PS-TestCons3.csv" -NoType -Delimiter ";" -Encoding UTF8
    
    
    
    

     
    Leider funktioniert das bei mir nicht wie gewünscht und die beiden Dateien werden nicht fortgeschrieben. Wo liegt mein Fehler?


    Donnerstag, 12. Mai 2016 08:54
  • Hallo,

    vielen Dank für die Antwort. Meine Kenntnisse in PS sind bisher noch stark begrenzt. Deshalb brauche ich einen Moment um mich dazu einzulesen und das auszutesten.

    Wir verwenden auf unseren Servern V1.0 und das wird wohl auch eine Weile so bleiben..:-/

    Donnerstag, 12. Mai 2016 09:03
  • Hallo,

    ich hab grad mal kurz nachgesehen, es sieht so aus, als gäbe es Convertto-csv noch nicht in der Version 1. Aber du kannst es trotzdem mal versuchen, falls ich mich irre.

    Andererseits kannst du das Cmdlet auch in einer Funktion teilweise nachbauen. Les mal bzgl. StreamReader und StreamWriter nach, das sind .Net-Inhalte, die du aber auch in PS nutzen kannst.

    Mit dem StreamReader kannst du die erste Zeile der alten Datei lesen. Damit hast du den Tabellenkopf. Wenn du den mit -split trennst, hast du die benötigten Felder in der richtigen Reihenfolge. Angenommen, das steht in der Variablen "$Header" als Array.

    Wenn du dein $Cons1-Array hast, kannst du für jedes Element $o (foreach-Schleife) folgendes machen:

    ($Header | foreach{$o.$_}) -join ";"

    Das wäre die neue Zeile. Die kannst du dann mit einem StreamWriter oder Add-Content an die Zieldatei anfügen.

    Außerdem wäre es prinzipiell gut, wenn du schreibst, was genau nicht funktioniert; also Fehlermeldungen oder Ergebnis-Beschreibung. Dass es nur nicht wie gewünscht funktioniert, sagt uns hier nicht viel.

    Donnerstag, 12. Mai 2016 12:35
  • Probier mal bitte das:

    $Cons1old = Import-CSV "$path\PS-TestCons2.csv" -Delimiter ';' -Encoding UTF8 
    $Cons1 = ($finalCSV_QServer| Sort-Object description -unique)
    $Cons1old + $Cons1 | Export-CSV "$path\PS-TestCons2.csv" -NoTypeInformation -Delimiter ';' -Encoding UTF8
    
    $Cons2old = Import-CSV "$path\PS-TestCons3.csv" -Delimiter ';' -Encoding UTF8 
    $Cons2 = ($finalQDBODBC| Sort-Object description -unique)
    $cons2old + $Cons2 | Export-CSV "$path\PS-TestCons3.csv" -NoTypeInformation -Delimiter ';' -Encoding UTF8

    Beim Zusammenfügen hat du erst das neue und dann das alte addiert, damit wird die Datei natürlich nicht fortgeschrieben. Und Import-Csv hat kein "-NoType"


    • Bearbeitet psott Donnerstag, 12. Mai 2016 13:24
    Donnerstag, 12. Mai 2016 13:23
  • Vielen Dank für den Hinweis.Theoretisch müssten die beiden Dateien "PS-TestCons2" und "PS-TestCons3" jedes mal größer werden, wenn ich das Skript ausführe, oder habe ich gerade einen Denkfehler?! Aktuell wird mit dieser Konstruktion, jede Datei bei erneutem Ausführen des Skriptes wieder neu erzeugt und alte Datenbestände werden überschrieben.
    Freitag, 13. Mai 2016 10:44
  • Hallo,

    ich habe nun mal folgendes probiert:

    #Wie bekommt man hier eine fortschreibende Ausgabe in eine Datei mit Powershell V1?
    
     $pathCons1 = "...\PS-TestCons1.csv"
      $pathCons2 = ".....\PS-TestCons2.csv"
    
    
    $Cons1 = ( $finalCSV_QServer | sort description -unique)
      $newDat1 = New-Object -typename System.IO.StreamWriter($pathCons1, "true");
      $lines= $newDat1.Split("`n")
        foreach($o in $Cons1){
        $newDat1.Write("$Cons1"-join "`r`n")
        $newDat1.Close();}
    
    $Cons2 = ($finalQDBODBC| sort description -unique)
      $newDat2 = New-Object -typename System.IO.StreamWriter($pathCons2, "true");
        foreach($o in $Cons2){
        $newDat2.Write("$Cons2"-join "`r`n")
        $newDat2.Close();}
    

    Der Streamwriter ergänzt die bestehende Datei mit den neuen Daten, allerdings ergänzt er mir den ganzen Inhalt immer als eine einzige Zeile.

    Wie kann ich die Ausgabe bzw. die Zeilen umbrechen?

    Mittwoch, 18. Mai 2016 09:29
  • Hallo,

    der StreamWriter hat auch eine WriteLine-Methode, die jeweils eine Zeile schreibt. Ich verstehe aber nicht, warum du $Cons1 in die Schleife schreibst und nicht $o:

     foreach($o in $Cons1)
    {
        $newDat1.WriteLine($o)
    }
    $NewDat1.Close()

    Ich weiß jedoch nicht, wie $o - sprich ein Datensatz deines $Cons1-Arrays - aussieht. Du musst es vorher in einen String convertieren. Und du musst sicherstellen, dass die Spalten in der richtigen Reihenfolge sind. Dafür hatte ich schon mal eine Zeile weiter oben gepostet.

    Prinzipiell würde ich aus Verwechslungsgründen einen StreamWriter nicht $newDat nennen, sondern $Writer. Außerdem solltest du nich "$Cons1" in Anführungszeichen verwenden, weil dadurch das Array kaputt geht. Variablen werden nicht in Anführungsstrichen gesetzt, außer man möchte danach nichts mehr mit ihnen machen (d.h. nur für die reine Ausgabe!).

    Mittwoch, 18. Mai 2016 09:59
  • Hallo,

    ich muss gestehen dass ich nicht weiterkomme.

    Die Überschrift meines Datensatzes sieht folgendermaßen aus:

    $header = @("index" , "starttime" , "messageid" , "process" , "hostname" , "ipadress" , "xxx" , "xxx","xxx" ,"xxx" ,"xxx" ,"logkind","xxx","xxx","xxx","xxx")

    Das heißt 15 Spalten und fortschreibende Zeilen im CSV-Format.

    Soweit ich das richtig verstanden habe muss ich $header als StreamReader einlesen

     $StreamReader = New-Object -typename System.IO.StreamWriter($header)

    und dann den Inhalt von $Cons1 hinzufügen:

    ($header | foreach{$o.$Cons1}) -join ";"

    Ist das so korrekt?

    Wie bekomme ich den Zusammenhang zu dem StreamWriter?:

    $Cons1 = ( $finalCSV_QServer | sort description -unique)
     $StreamWriter1 = New-Object -typename System.IO.StreamWriter($pathCons1, "true")
        foreach($o in $Cons1)
        {
         $StreamWriter1.Write($o -join "`r`n")
        }
         $StreamWriter1.Close()

    Montag, 23. Mai 2016 07:39
  • Hallo,

    ein StreamReader liest Dateien ein, keine Arrays oder anderen Variablen. Deshalb musst du einen Dateipfad an den Streamreader übergeben. 

    Ich habe den Code selbst schnell zusammengeschrieben:

    $dir = Split-Path $MyInvocation.MyCommand.Path
    
    #Pfad der zu ergänzenden Datei
    $CSVDateiPfad = "$dir\Daten.csv"
    
    #die hinzuzufügenden Daten (hier kommt deine Abfrage hin)
    $NeueDaten = Import-Csv "$dir\NeueDaten.csv" -Delimiter ";"
    
    #StreamReader öffnen und Tabellenkopf lesen
    $Reader = New-Object System.IO.StreamReader($CSVDateiPfad) 
    $Header = $Reader.ReadLine()
    $AusgabeFelder = $Header -split ";"
    
    #StreamReader wieder schließen
    $Reader.Close()
    
    #StreamWriter öffnen
    $Writer = New-Object System.IO.StreamWriter($CSVDateiPfad, $true)
    
    #Daten zeilenweise anfügen
    foreach($d in $NeueDaten)
    {
        #Ausgabezeile formen; Reihenfolge durch $AusgabeFelder sichergestellt
        $Zeile = ($AusgabeFelder | foreach{$d.$_}) -join ";"
        $Writer.WriteLine($Zeile)
    }
    
    $Writer.Close()

    In $csvDateiPfad muss der Pfad der AusgabeDatei stehen. $NeueDaten muss deine neu generierten Daten enthalten; als Beispiel habe ich eine csv-Datei importiert.

    Durch das $true wird der StreamWriter in einen Anfügemodus versetzt, sodass er die bisherige Datei nicht überschreibt.

    Dieser Code, den du geschrieben hast:

    ($header | foreach{$o.$Cons1}) -join ";"

    ist völlig sinnlos. Das meine ich nicht bösartig, aber du solltest dich unbedingt mehr mit Programmierung beschäftigen, wenn du derartige Projekte umsetzen willst. $Cons1 ist ein Array von Objekten. $o wäre, wie ich es oben definiert hatte, ein einzelner Datensatz aus diesem Array. Du kannst hinter dem Punkt nur eine Eigenschaften-Bezeichnung stehen haben oder eine Variable, die eine Eigenschaftenbezeichnung enthält. Nie ein Array!! Außerdem muss in einer foreach-Objekt-Schleife immer der Operator "$_" vorkommen, sonst ist sie per se falsch, da du ja das jeweilige Schleifenobjekt überhaupt nicht aufrufst.

    Viele Grüße

    Christoph

    • Als Antwort vorgeschlagen Tschenn1 Dienstag, 24. Mai 2016 12:10
    • Nicht als Antwort vorgeschlagen Tschenn1 Dienstag, 24. Mai 2016 12:10
    • Als Antwort vorgeschlagen hpotsirhc Donnerstag, 26. Mai 2016 11:52
    • Als Antwort markiert Denniver ReiningMVP, Moderator Samstag, 28. Mai 2016 21:43
    Montag, 23. Mai 2016 09:04
  • Hallo Christoph,

    wow vielen Dank, die Dateien werden fortgeschrieben wie gewünscht.

    Dass ich Nachholbedarf in Powershell habe weiß ich, ich arbeite daran  ;-)

    Vielen vielen Dank für deine Hilfe

    Dienstag, 24. Mai 2016 12:11
  • Hallo Tschenn1,

    toll, dass es funktioniert. Wegen der anderen Version kann man ja nie ganz sicher sein.

    Aber irgendwo muss man ja anfangen und man wächst mit den Aufgaben.

    Könntest du noch die passenden Posts als Antwort markieren?

    Viele Grüße

    Christoph

    Mittwoch, 25. Mai 2016 06:48
  • Die Antwort wurde bereits markiert.

    Vielen Dank noch einmal!!

    Montag, 30. Mai 2016 12:16