none
Datei mit Powershell verändern RRS feed

  • Frage

  • Hallo zusammen,

    ich bin bisher noch kompletter Neuling bei PowerShell und habe nun ein Problem was ich gerne mit PowerShell lösen würde.

    Ich habe eine Datei mit sehr großem Inhalt. In dieser Datei möchte Zeilen hinzufügen und ergänzen.

    Die Datei lese ich wie folgt ein:

                $File = Get-Content $FILE_PATH
    
                for ($i=0; $i -lt $File.length - 1; $i += 1) 
                {
                    If ($File[$i].Contains('Suchstring'))
                    {
                        $ColM2_C1 = $i
                    }
                }

    Auf diese Weise bekomme ich die Zeile in der mein Suchstring steht.

    Nun will ich prüfen ob in der nächsten Zeile ein bestimmter String steht. Dies würde ich wieder über eine If-Abfrage realisieren.

    IF ($File[ColM2_C1 + 1].Contains('Suchstring'))
    {
    }

    Je nachdem Ergebnis meiner Abfrage möchte ich dann diese Zeile löschen oder an dieser Stelle eine Zeile ergänzen.

    Wie aber ist es möglich in PowerShell eine bestimmte Zeile zu löschen oder an einer bestimmten Zeile einen String zu ergänzen?

    Gruß Marc

    Dienstag, 1. April 2014 13:54

Antworten

  • Hallo 0Marc0!

    Auch auf die Gefahr hin dich zu verwirren, empfehle ich dir bei den PowerShell Möglichkeiten zu bleiben.

    .NET Klassen zu Programmieren sollte die letzte Möglichkeit sein.

    Lies bitte mal meinen Artikel über Get-Content hier:
    http://www.powershell-group.eu/powershell-get-content/

    Dann wird dir vielleicht einiges klarer.

    Da Get-Content und Set-Content mit Verschiedenen Providern (Registry,Filesystem,Certificate…) arbeiten können, sind einige Parameter dieser Cmdlets Dynamisch.

    Dynamische Parameter sind im jeweiligen Provider beschrieben und nicht bei dem Cmdlet mit dem man gerade Arbeitet.
    Du Arbeitest hier mit dem FileSystem Provider der beitet für diese Cmdlets den Parameter –Encoding dort kann man das Encoding festlegen.

    Siehe: http://technet.microsoft.com/en-us/library/hh847764.aspx (DYNAMIC PARAMETERS: Encoding)

    Du kannst mit Get-Content und Set-Content das -Encoding Unicode benutzen!

    Unicode = Encodes in UTF-16 format using the little-endian byte order.

    Ich würde es so machen:

    # Folgende Zeile liest die Datei # Die Klammern () um Get-Content sorgen dafür das die Datei komplett in den Speicher geholt wird # damit wir ein File-lock vermieden (Get-Content $FILE_PATH -Encoding Unicode) | ForEach-Object { # Die Aktuelle Text-Zeile befindet sich in der Automatisch generierten Variable $_ $Zeile = $_ # Text in der Aktuellen Zeile und in der Zeile -1 suchen # Ich nutze -Like weil es für gross und Kleinschreibung Funktioniert! If($Zeile -like "*Suchtext Aktuelle Zeile*" -and $AlteZeile -Like "*Suchtext Aktuelle Zeile -1*") { # Textverarbeitung hier machen!!

    "Gefunden: $_" } $AlteZeile = $_ } | Set-Content -Path $FILE_PATH -Encoding Unicode



    PowerShell Artikel, Buchtipps und kostenlose PowerShell Tutorials + E-Books
    auf der deutschsprachigen PowerShell Community

    Mein 21 Teiliger PowerShell Video Grundlehrgang
    Deutsche PowerShell Videos auf Youtube
    Folge mir auf:
    Twitter | Facebook | Google+

    Mittwoch, 2. April 2014 08:20
  • Hallo Peter,

    habe es jetzt mal so gelöst:

                $Utf16Encoding = New-Object System.Text.UnicodeEncoding($False,$true)
                [System.IO.File]::WriteAllLines($PATH, $Conf, $Utf16Encoding)

    Deine Möglichkeit ist wahrscheinlich eleganter, daher werde ich deinen Vorschlag auch noch testen.

    Danke für deine Hilfe.

    • Als Antwort markiert Alex Pitulice Dienstag, 22. April 2014 09:44
    Mittwoch, 2. April 2014 09:51

Alle Antworten

  • Get-Content liest eine Datei komplett in den Speicher und ergänzt jede Zeile mit einigen weiteren Informationen (Get-Member zeigt diese an). Somit kannst Du die die Daten zwar manipulieren, aber eben nur im Speicher. Nach der Veränderung müßtest Du die Daten wieder schreiben, am besten in eine neue Datei. Die gleiche Datei zu lesen und zu verändern führt meist zu Problemen.

    Wenn Du somit die Daten liest und in eine andere Datei schreibst, sollte die Anforderung auch einfacher zu erfüllen sein.

    Was heißt bei Dir „sehr großem Inhalt“? Wenn die Daten in die GB gehen, solltest Du auf Get-Content verzichten und statt dessen die Klasse StreamReader verwenden. Diese ist bei großen Datenmengen um ein Vielfaches schneller.

    Folgender Auszug aus einem meiner Beiträge in einem anderen Forum könnte helfen:

    Sorry, but I do not want to agree J. ForEach-Object is not the issue here but Get-Content

     

    “Get-Content –ReadCount 0” behaves similar to “$sr.ReadToEnd()”. This is what the documentation says:

     

    Specifies how many lines of content are sent through the pipeline at a time. The default value is 1. A value of 0 (zero) sends all of the content at one time.

     

    And this explains why the PowerShell process grows pretty much. Get-Content can deal with very large files. When $sr.ReadToEnd() throws an OutOfMemoryException Get-Content still can read the file into memory, which is usually not required.

     

    ReadCount 1 is the default and is extremely slow. I guess this is for all the additional properties being added to all the strings:

     

    PS C:\> (Get-Content C:\Windows\WindowsUpdate.log)[0] | fl * -Force

     

    PSPath       : C:\Windows\WindowsUpdate.log

    PSParentPath : C:\Windows

    PSChildName  : WindowsUpdate.log

    PSDrive      : C

    PSProvider   : Microsoft.PowerShell.Core\FileSystem

    ReadCount    : 1

    Length       : 80

     

    Can we somehow disable this behavior if the information is not needed?

     

     

    I have did some test and I still think, that the StreamReader is the best choice.

     

    Info = Runtime, WorkingSet, VirtualMemorySize

     

    46 MB File

    Test                     Info

    ----                     ----

    Get-Content -ReadCount 0 00:00:00.5560555 -      169,43 MB -      609,03 MB

    Get-Content -ReadCount 1 00:00:12.3112938 -       80,96 MB -      609,38 MB

    StreamReader ReadLine    00:00:01.1844469 -       69,79 MB -      609,57 MB

    StreamReader ReadToEnd   00:00:00.3521329 -      104,02 MB -      609,07 MB

     

     

    180 MB File

    Test                     Info

    ----                     ----

    Get-Content -ReadCount 0 00:00:01.6994242 -      512,54 MB -    1.122,27 MB

    Get-Content -ReadCount 1 00:00:51.7975485 -       81,15 MB -      608,37 MB

    StreamReader ReadLine    00:00:04.0070396 -       69,83 MB -      609,34 MB

    StreamReader ReadToEnd   00:00:00.7822951 -      175,84 MB -      866,22 MB

     

    1.1 GB File

     

    Test                     Info

    ----                     ----

    Get-Content -ReadCount 0 00:00:10.6040012 -    1.318,21 MB -      347,71 MB

    Get-Content -ReadCount 1 00:05:18.1970685 -       81,18 MB -      606,86 MB

    StreamReader ReadLine    00:00:23.4138347 -       68,06 MB -      608,32 MB

    StreamReader ReadToEnd   {Exception calling "ReadToEnd" with "0" argument(s): "Exception of type 'System.OutOfMemoryException' was thrown.", At li...

     

    -Raimund


    -Raimund

    Dienstag, 1. April 2014 19:23
  • Hallo Raimund,

    danke für deine Antwort.

    Gemessen an deinen Beispielen handelt es sich nur um eine kleine Datei.

    Filegröße ca. 2MB und 7000 Zeilen.

    Ich denke für diese Zecke sollte Get-Content noch ausreichend sein.

    Wenn ich die neuen Zeilen dann in ein neues File schreibe wie könnte ich dann hierfür vorgehen?

    Also eine bestimmte Zeile hinzufügen oder löschen?

    Dies in der gleichen Datei zu machen ist gar nicht möglich?

    Mittwoch, 2. April 2014 04:32
  • Also ich versuche jetzt mit dem StreamWriter das File zu schreiben.

    Dabei habe ich aber ein Problem. Ich habe jetzt mehrere Encodings getestet. Aber egal was ich beim Encoding angebe, wenn ich die Datei in einem Editor öffne wird Unicode (UTF8 NB) angezeigt.

    Ich bräuchte als Encoding Unicode (UTF16 LE).

    Das schreiben realisiere ich wie folgt:

    $stream = New-Object System.IO.StreamWriter ($PATH, [System.Text.Encoding]::UTF16LE)
    
    
    for ($i=0; $i -lt $Conf.length; $i += 1) 
        {
            $stream.WriteLine($Conf[$i])
        }
        
    $stream.Close() 
    
    Hat jemand eine Idee woran das liegen könnte?
    Mittwoch, 2. April 2014 06:07
  • Hallo 0Marc0!

    Auch auf die Gefahr hin dich zu verwirren, empfehle ich dir bei den PowerShell Möglichkeiten zu bleiben.

    .NET Klassen zu Programmieren sollte die letzte Möglichkeit sein.

    Lies bitte mal meinen Artikel über Get-Content hier:
    http://www.powershell-group.eu/powershell-get-content/

    Dann wird dir vielleicht einiges klarer.

    Da Get-Content und Set-Content mit Verschiedenen Providern (Registry,Filesystem,Certificate…) arbeiten können, sind einige Parameter dieser Cmdlets Dynamisch.

    Dynamische Parameter sind im jeweiligen Provider beschrieben und nicht bei dem Cmdlet mit dem man gerade Arbeitet.
    Du Arbeitest hier mit dem FileSystem Provider der beitet für diese Cmdlets den Parameter –Encoding dort kann man das Encoding festlegen.

    Siehe: http://technet.microsoft.com/en-us/library/hh847764.aspx (DYNAMIC PARAMETERS: Encoding)

    Du kannst mit Get-Content und Set-Content das -Encoding Unicode benutzen!

    Unicode = Encodes in UTF-16 format using the little-endian byte order.

    Ich würde es so machen:

    # Folgende Zeile liest die Datei # Die Klammern () um Get-Content sorgen dafür das die Datei komplett in den Speicher geholt wird # damit wir ein File-lock vermieden (Get-Content $FILE_PATH -Encoding Unicode) | ForEach-Object { # Die Aktuelle Text-Zeile befindet sich in der Automatisch generierten Variable $_ $Zeile = $_ # Text in der Aktuellen Zeile und in der Zeile -1 suchen # Ich nutze -Like weil es für gross und Kleinschreibung Funktioniert! If($Zeile -like "*Suchtext Aktuelle Zeile*" -and $AlteZeile -Like "*Suchtext Aktuelle Zeile -1*") { # Textverarbeitung hier machen!!

    "Gefunden: $_" } $AlteZeile = $_ } | Set-Content -Path $FILE_PATH -Encoding Unicode



    PowerShell Artikel, Buchtipps und kostenlose PowerShell Tutorials + E-Books
    auf der deutschsprachigen PowerShell Community

    Mein 21 Teiliger PowerShell Video Grundlehrgang
    Deutsche PowerShell Videos auf Youtube
    Folge mir auf:
    Twitter | Facebook | Google+

    Mittwoch, 2. April 2014 08:20
  • Hallo Peter,

    habe es jetzt mal so gelöst:

                $Utf16Encoding = New-Object System.Text.UnicodeEncoding($False,$true)
                [System.IO.File]::WriteAllLines($PATH, $Conf, $Utf16Encoding)

    Deine Möglichkeit ist wahrscheinlich eleganter, daher werde ich deinen Vorschlag auch noch testen.

    Danke für deine Hilfe.

    • Als Antwort markiert Alex Pitulice Dienstag, 22. April 2014 09:44
    Mittwoch, 2. April 2014 09:51