Momentan befindet sich dieses Kapitel im Aufbau  
Fehler und Unklarheiten bitte ich daher zu entschuldigen

(Stand: 15. Januar 2012)

 

Behandelte Themen im Kapitel "Textmanipulationen und Reguläre Ausdrücke"

     0. Einleitung
         Beispiel 1a: Erzeugung eines Arrays aus alphanumerischen Zufallstrings
         Beispiel 1b: Erzeugen eines Testarrays mit vorgegebenen Zufallswerten
         Beispiel 2: Erzeugen einiger Beispieldateien 

     1. Unstrukturierte Textdateien
         1.1 Inhalt einer Textdatei in einer Variable speichern 
               1.1.1 Einlesen kleiner und mittelgroßer Textdateien
                        Beispiel 1: Wie viele Zeilen hat die Testdatei  (get-content)
                        Beispiel 2: Wie viele Zeilen hat die Testdatei  ([System.IO.File])
               1.1.2 Einlesen großer Dateien
                        Beispiel 1: Wie viele Zeilen hat eine Datei ${}
                        Beispiel 2: Wie viele Zeilen hat eine Datei  (get-content -readcount 0) 
                        Beispiel 3: Wie viele Zeilen hat eine Datei  (System.IO.StreamReader - ReadToEnd) 
         1.2 Inhalt einer Datei zeilenweise bearbeiten
               1.2.1 Analyse einer Datei mit Select-String
                         Beispiel 1: Alle Zeilen mit einem Suchbegriff aus einer Datei filtern
                         Beispiel 2: Alle Dateien, die einen Suchbegriff enthalten, finden
                1.2.2 Zeilenweise Bearbeiten einer Datei (Streamreader - ReadLine) 
                         Beispiel 1: Zeilenweises Parsen und Analysieren einer Datei
         1.3 gesamten Text auf einmal bearbeiten (StreamReader - ReadAllText)
               Beispiel 1: Ersetzen eines Suchbegriffs in der gesamten Datei
               Beispiel 2: Wie oft kommt ein Suchstring in einer Datei vor?
         1.4 Zeilenübergreifende Textanalyse
               Beispiel 1: Ausgabe der passenden Zeile, sowie der Vorgänger- und Nachfolgerzeile
               Beispiel 2a: Suchen nach Textstellen, bei denen ein Suchbegriff in zwei aufeinanderfolgenden Zeilen vorkommt
               Beispiel 2b: Suchen nach Textstellen, bei denen ein Suchbegriff in drei aufeinanderfolgenden Zeilen vorkommt

     2. Vergleichsoperatoren
         2.1 Die Operatoren -Match, -NotMatch, -CMatch, -IMatch
               Beispiel 1a: Der Matchoperator auf ein Skalar angewendet
               Beispiel 1b: Der Matchoperator auf ein Array angewendet
               Beispiel 2: Auf welchen Positionen im Array liegt ein Treffer
               Beispiel 3: Der NotMatch Operator 
               Beispiel 4: Der NotMatch Operator zum Zahlenvergleich
         2.2 Die Operatoren -Like und -NotLike
               Beispiel 1: Vergleich zwischen -Like und -Match
         2.3 Die Operatoren -Contains und -NotContains
               Beispiel 1: Der -Contains Operator
         2.4 Die Operatoren -eq und -ne
               Beispiel 1: Vergleich -eq zu -contains

     3. Der System.Text.RegularExpressions-Namespace
         3.1 Einleitung
               Beispiel 1: Durchsuchen eines Strings auf alle Übereinstimmungen
         3.2 Regex-Klasse
               Beispiel 1a: statische Methoden der Klasse Regex
               Beispiel 1b: Instanzbasierte Methoden der Klasse Regex
               3.2.1 Mit der Regex-Klasse instanzbasiert und instanzfrei arbeiten
                        Beispiel 1: Erstellen einer Instanz der Regex-Klasse ohne Optionen
                        Beispiel 2: Erstellen einer Instanz der Regex-Klasse mit Optionen
                        Beispiel 3a: Einen Satz in Wörter splitten mit der statischen Split-Methode
                        Beispiel 3b: Einen Satz in Wörter splitten mit der Split-Methode (Instanz)
                        Beispiel 3c: Einen Satz in Wörter splitten mit dem Split-Operator
               3.2.2 Regex-Methoden
                        Beispiel 1a: Prüfen, ob ein String eine formal gültige IP-Adresse darstellt
                        Beispiel 1b: Prüfen, ob ein String eine formal gültige email-Adresse darstellt
                        Beispiel 3: Durchsuchen eines Strings auf alle Übereinstimmungen (Matches-Methode) 

      4. Verschlüsselung von Strings
          Beispiel 1: Ver- und Entschlüsseln eines Strings

 

************************************************************************************

0 Einleitung

Um etwas Testmaterial zur Verfügung zu haben, benutze ich zur Erzeugung eines Arrays mit zufälligen Strings eine Funktion aus dem Kapitel Die Variablen  -> 1.1.2 explizite Typzuweisung -> Beispiel 3. Die Funktion ist dort auch näher erklärt

Beispiel 1a: Erzeugung eines Arrays aus alphanumerischen Zufallstrings

Function get-alphanumericRandom{
    param($digits=8,[string]$flag='111', $SpaceFrequency=4,[boolean]$LineFeed=$false)
   $RandomPool=$null

   $flag=[Convert]::ToInt32($flag, 2)

   $min_lower=[int][char]'a' #97
   $max_lower=[int][char]'z' #122

   $min_upper=[int][char]'A'  #65
   $max_upper=[int][char]'Z'  #90

   $min_number=[int][char]'0' #48
   $max_number=[int][char]'9' #57

   $space =[int][char]' ' #32

   if($flag -band 1){
     $randompool=[char[]]($min_lower..$max_lower)
   }
 
   if($flag -band 2){
     $randompool+=[char[]]($min_upper..$max_upper)
   }
 
   if($flag -band 4){
     $randompool+=[char[]]($min_number..$max_Number)
     }

   if($flag -band 8){
     for($i=1;$i -le $spaceFrequency;$i++){
       $randompool +=" "
     } #for
   }
 
 
   #Benutzerdefinierte zusätzliche Zeichenketten
   for($i=1;$i -le 3;$i++){
       $randompool +="Karl"
       $randompool +="Napf"
   }
   for($i=1;$i -le 2;$i++){
       #if($linebreak){$randompool +="`n"} #Zeilenumbruch
       if($linefeed){$randompool +=[Environment]::NewLine}
     }

  $ofs = ''

  [string](get-random -input $randompool -count $digits)
}#function Ende

$a=@()
for ($i=0;$i-lt 5;$i++){
  $value=get-alphanumericRandom -digits 75 -flag 1111
  $a += $value
  }
$a

#mögliche Ausgabe

jn1bKarlA KarlervPDBVs0WMNxkgdIc 67USKXf824QYGNapfwRmtT olhuCJpqF9KarlNapfH5aLEi yO3NapfZz
oeHBKarlY u 5r aKarlNapfx8WANapfFml9VyPcbMX1UKarlsDg7Qp O4iS3LtENapfKvkIG62zCfR0qndhJjTZwN
GIpKLj3nKarlPocQf08WNapf BlkNKarlV TqNapfSHe2gaYJz DNapfF6CsXAROhwKarl14uimbvy7U9E5rt MZxd
sWtdiNyBZVSx4GaMhmHC9gb2YKarlTu5LKarl6R 0XNapfJ 7K Napfn QNapfDKarleE3AcIFjvk8q1wUOlofpPzr
NapfWsjkMKarlQqDNapfTNZ3r HGJgobRAwm FKarlyL4dxlNapfpP8iaKarl EVUz0fnOScKt6X1uY97B h25eICv


Damit ihr nicht immer diese große Funktion mitschleppen müsst, könnt ihr euch das TestArray auch einfach so wie im folgenden Beispiel erzeugen

Beispiel 1b: Erzeugen eines Testarrays mit vorgegebenen Zufallswerten

$a=@()
$a+="jn1bKarlA KarlervPDBVs0WMNxkgdIc 67USKXf824QYGNapfwRmtT olhuCJpqF9KarlNapfH5aLEi yO3NapfZz"
$a+="oeHBKarlY u 5r aKarlNapfx8WANapfFml9VyPcbMX1UKarlsDg7Qp O4iS3LtENapfKvkIG62zCfR0qndhJjTZwN"
$a[0]
$a[1]

#Ausgabe

jn1bKarlA KarlervPDBVs0WMNxkgdIc 67USKXf824QYGNapfwRmtT olhuCJpqF9KarlNapfH5aLEi yO3NapfZz
oeHBKarlY u 5r aKarlNapfx8WANapfFml9VyPcbMX1UKarlsDg7Qp O4iS3LtENapfKvkIG62zCfR0qndhJjTZwN

Mit diesen beiden Elementen des Arrays $a will ich in diesem Kapitel arbeiten

 

Beispiel 2: Erzeugen einiger Beispieldateien

Das folgende Skript erzeugt die mehrere Texdateien unter "C:\temp\" mit 100, 1000, 10000 und mehr Zeilen aus zufällig Strings. Gelegentlich kommen die beiden Suchstring " Error: 220 " und " Error: 250 " vor.
Das Skript basiert auf der gleichen Grundlage wie Beispiel 1a.

Function get-alphanumericRandom{
   param($digits=8,[string]$flag='111', $SpaceFrequency=4,[boolean]$LineFeed=$false,$Searchstring=" Error: 225 ")
   $RandomPool=$null

   $flag=[Convert]::ToInt32($flag, 2)

   $min_lower=[int][char]'a' #97
   $max_lower=[int][char]'z' #122

   $min_upper=[int][char]'A'  #65
   $max_upper=[int][char]'Z'  #90

   $min_number=[int][char]'0' #48
   $max_number=[int][char]'9' #57

   $space =[int][char]' ' #32

   if($flag -band 1){
     $randompool=[char[]]($min_lower..$max_lower)
   }
 
   if($flag -band 2){
     $randompool+=[char[]]($min_upper..$max_upper)
   }
 
   if($flag -band 4){
     $randompool+=[char[]]($min_number..$max_Number)
     }

   if($flag -band 8){
     for($i=1;$i -le $spaceFrequency;$i++){
       $randompool +=" "
     } #for
   }
 
   #Anpassen zusätzliche Zeichenketten
   #write-host $randompool.count
   for($i=1;$i -le 2;$i++){
    $randompool+=$randompool #Verdoppelung des Zufallpools von 69 auf 138. Dadurch kommt der String ErrorID:220 seltener vor
    }
   #write-host $randompool.count
     
   for($i=1;$i -le 1;$i++){
       $randompool +=$Searchstring
       $randompool +=" Error: 250 "
   }
   for($i=1;$i -le 2;$i++){
       #if($linebreak){$randompool +="`n"} #Zeilenumbruch
       if($linefeed){$randompool +=[Environment]::NewLine}
     }

  $ofs = ''

  [string](get-random -input $randompool -count $digits)
}#function Ende


function BigFile{
   param($extension="txt",$NumberFiles=1,[int32]$NumberLines=150,$filename="Big")
       new-item -type "file" -path "$path" -name "$filename.$extension" -force
       out-file -filepath "$path\$filename.$extension" -append -inputobject $(get-date) -width 80
         for($j=1;$j -le $NumberLines; $j++){
            $text=get-AlphanumericRandom -digits 80 -flag 1111 -SpaceFrequency 7 -Linefeed $false
            $text=$(get-date -format g)+ "   "+$text
             out-file -filepath "$path\$filename.$extension" -append -inputobject $text -width 80 -encoding unicode
       } #for j
     }

#Aufruf
 $path="C:\temp"
 
 bigfile -numberLines 100 -Filename File_20Kb         #ca. 20  Kb - Datei
 bigfile -numberLines 1000 -Filename File_200Kb       #ca. 200 Kb - Datei
 bigfile -numberLines 10000 -Filename File_2MB        #ca. 2 Mb - Datei
 #bigfile -numberLines 100000 -Filename File_20MB      #ca. 20 Mb - Datei  (dauert mehrere Minuten)
 #bigfile -numberLines 200000 -Filename File_40MB      #ca. 40 Mb - Datei  (dauert mehrere Minuten)
 #bigfile -numberLines 1000000 -Filename File_500MB    #ca. 200 Mb - Datei (dauert mehrere Minuten)

Die Erzeugung von Dateien im Megabytebereich kann einige Minuten dauern.

 

1 unstrukturierte Textdateien

Eine häufige Aufgabe in der IT ist die Analyse von Textdateien. Meist handelt es sich dabei um textbasierte Logs, die in Form teilweiser recht großer Textdateien vorliegen, oft auf mehrere Textdateien verteilt.
Im Gegensatz zu strukturierten Dateien wie XML oder CSV Dateien haben diese Dateien meist keine Struktur. 


Wiesooft bieten .Net und Powershell eine Reihe von Methoden an, eine Textdatei zu analysieren und gegebenenfalls zu verändern. Grundsätzlich kann man eine Textdatei
a) komplett in den Arbeitsspeicher laden und erst anschließend weitere Schritte auf die gewonnenen Daten durchzuführen
b) Zeile für Zeile zu lesen und zu bearbeiten. Anschließend wird die gelesene Zeile wieder verworfen

Die Methode a) bietet Vorteile, wenn eine Datei mehrfach nacheinander analysiert werden soll, oder wenn je nach dem Ergebnis der Analyse noch einmal die Vorgängerzeilen benötigt werden. Ebenso ist es sinnvoll eine Datei komplett zu laden, wenn die Datei als Ganzes untersucht werden soll.
Eine Datei komplett zu laden, benötigt erstmal Zeit und belegt Arbeitsspeicher. Die eingeladene Datei kann dafür umfangreich analysiert werden.

Die Methode b) kann man anwenden, wenn jede Zeile einmalig und unabhängig vom übrigen Inhalt der Datei analysiert werden soll.
Eine zeilenweise Analyse ist einfacher, benötigt weniger Arbeitsspeicher und für viele Aufgabenstellungen vollkommen ausreichend. Eine typische Aufgabenstellung wäre alle diejenigen Zeilen, die einen bestimmten Suchbegriff enthalten, auszugegeben.

 

1.1 Inhalt einer Textdatei in einer Variable speichern

Ich stelle vier Möglichkeiten vor, den Inhalt von Textdateien unter Powershell in den Arbeitsspeicher des Rechners einzulesen. Im Endergebnis liefern alle Methoden dasselbe Ergebnis, dennoch sind manche Methoden besser für kleinere, andere für große Textdateien geeignet

Hier nur ein kurzer Überblick. In den nachfolgenden Kapiteln behandle ich die Varianten dann detailliert.


a) $a = get-content "c:\temp\file_20kb.txt"
Dieser Befehl fügt jede neu Textzeile als Element dem Array a neu hinzu. 

b) $a = get-content "c:\temp\file_20kb.txt" -readcount 10
Beschleunigen lässt sich der Einlesevorgang durch Hinzufügen eines Readcounts. Dadurch werden hier zum Beispiel 10 Textzeilen als ein Arrayelement zu a hinzugefügt.

c) $a = [IO.File]::ReadAllText("c:\temp\file_20kb.txt")
Diese statische .Net-Methode liest das gesamte Textfile komplett ein.

d) $SR = new-object System.IO.StreamReader($FilePath)
    $a=$SR.ReadToEnd()

die StreamReader-Klasse bietet mehr Möglichkeiten als die File-Klasse

e)  $a = ${"c:\temp\file_20kb.txt"}
Man erhält jede Zeile als separates Arrayelement. Das Ergebnis ist identisch zu a)
 

Mit dem Skript 0 Einleitung -> Beispiel 2 habe ich einige Dateien verschiedener Größe erzeugt. Diese Textdateien enthalten neben Zufallsstrings am Beginn jeder Zeile einen Zeitstempel und zufällig verteilt die beiden Strings "Error: 220" und "Error: 225".
Die Beispieldateien sollen Logdateien simulieren


Die beiden folgenden Unterkapitel zeigen geeignete Methoden, um einerseits kleine bis mittlere, andererseits große Dateien einzulesen.

Große Textdateien definiere ich als Dateien mit einer Größe von mehr als 20 MB oder mehr als 100.000 Zeilen. Die Grenze ist willkürlich gezogen, weil auf meinem Testrechner ab etwa dieser Grenze bei einigen Methoden (Get-Content) die ersten Unterschiede auftauchen.
Die beiden Probleme sind der Zeitbedarf zum Einlesen, sowie Speicher(-RAM)Fehler.
Letztlich muß man selbst aus der Vielzahl der Möglichkeiten auswählen, welche Methode im konkreten Fall am schnellsten und zuverlässigsten arbeitet.

 

1.1.1 Einlesen kleiner und mittelgroßer Textdateien

Beispiel 1: Wie viele Zeilen hat die Testdatei  (get-content)

$FilePath="c:\temp\file_20kb.txt"
$a=get-content -path $FilePath
"Number of Lines: {0}" -f $(1+$a.Count)

#mögliche Ausgabe
Number of Lines: 105

Für kleinere und mittlere Dateien bietet das cmdlet get-content in dieser Form den größten Komfort, siehe Technet:  Get-Content. Für größere Dateien seht euch die anderen Beispiele hier an.

Man erhält ein Array, bei dem jedes Arrayelement eine Textzeile enthält.

Eine 2 MB große Textdatei war auf meinem Testrechner innerhalb 0.4 Sekunden eingelesen, eine 20 MB große Textdatei in gut 2 Sekunden.
Werden die Dateien noch größer, steigt der Zeitaufwand auch weiter und das Skript wird immer unkomfortabler. Daher sollte man bei größeren Dateien auf die im nächsten Kapitel 1.1.3 Einlesen großer Dateien gezeigten Methoden umsteigen.


Beispiel 2: Wie viele Zeilen hat die Testdatei  ([System.IO.File])

$FilePath="c:\temp\file_20kb.txt"
$a = [IO.File]::ReadAllText($FilePath)
$a=$a.split("`n")
$a.count

#mögliche Ausgabe
Number of Lines: 105

[IO.File]::ReadAllText($FilePath) liest den Inhalt der Textdatei komplett in die Variable a ein ein. Möchte man die Textdatei anschließend ebenso wie in Beispiel 1a als Array bearbeiten, so kann man den Text über den Split-Operator zeilenweise aufteilen.

Eine 2 MB große Textdatei war auf meinem Testrechner innerhalb 0.2 Sekunden eingelesen imd gesplittet, eine 20 MB große Textdatei in 0.5 Sekunden, also doch deutlich schneller als Beispiel 1
Werden die Dateien noch größer, verbraucht diese Methode viel Arbeitsspeicher. Besonders auf 32-Bit Systemen kommt es dann zu Skriptabbrüchen, wegen einem Speicherfehler.


Ein praktisches Beispiel in der mittleren Größenklasse ist das Logfile des Windows Update Services unter c:\windows\WindowsUpdate.log, welches auf den meisten Windowsrechnern existieren dürfte.

 

1.1.2 Einlesen großer Dateien

Die folgenden Beispiele habe ich an einer Textdatei mit 1.000.000 Zeilen und einer Größe von etwa 200 MB Größe getestet.
Ob Textfiles in dieser Größenordnung sinnvoll sind, sei dahingestellt. Da solche Dateien aber vorkommen, sollte man Werkzeuge für deren Behandlung zur Verfügung haben.


Beispiel 1: Wie viele Zeilen hat eine Datei ${}

$a=${c:\temp\file_200MB.txt}

"Inhalt der Zeile/Arrayelement 1125 {0}: " -f $a[1125]
"Anzahl Zeilen/ Arrayelemente {0}/ {1}" -f $(1+$a.count),$a.count

#mögliche Ausgabe

Inhalt der Zeile/Arrayelement 1125 22.12.2011 21:28    Xj 1  lYE9C ...2 Error: 225 NU06 WfV0zrbl  iD:
Anzahl Zeilen/ Arrayelemente 1000002/ 1000001

Jede Textzeile ist einem Arrayelement zugeordnet. Das Einlesen der Datei erfolgt etwas schneller als im nächsten Beispiel. Als Nachteil ist mir aufgefallen, dass sich der Pfad zwischen den geschweiften Klammern ${...} nicht mit einer Variablen parametrisieren lässt.


Beispiel 2: Wie viele Zeilen hat eine Datei  (get-content -readcount 0)

Über den Parameter -readcount hat man die Möglichkeit festzulegen, wieviele Textzeilen in ein Arrayelement aufgenommen werden.

$FilePath="c:\temp\File_200MB.txt"
$a = get-content -path $filepath -readcount 0

"Inhalt der Zeile/Arrayelement 1125 {0}: " -f $a[1125]
"Anzahl Zeilen/ Arrayelemente {0}" -f $(1+$a.count)

 

Beispiel 3: Wie viele Zeilen hat eine Datei  (System.IO.StreamReader - ReadToEnd)

$FilePath="c:\temp\File_200MB.txt"
$SR = new-object System.IO.StreamReader($FilePath)
$a=$SR.ReadToEnd()
$a=$a.split("`n")
"Inhalt der Zeile/Arrayelement 1125 {0}: " -f $a[1125]
"Anzahl Zeilen/ Arrayelemente {0}" -f $a.count

Sehr schnell arbeitet auch die StreamReader-Klasse mit der Methode ReadToEnd

 

1.2 Inhalt einer Datei zeilenweise bearbeiten

Um eine Textdatei zeilenweise abzuarbeiten, bietet Powershell

a) eine einfache Methode mit dem cmdlet Select-String an. Mit diesem cmdlet können sowohl einfache Strings, wie auch ganze Textdateien durchsucht werden. Zurückgegeben wird der gesamte String, der den Suchstring enthält.

b) über .Net bietet mit der StreamReader-Klasse verschiedene Methoden an, um eine Textdatei zeilenweise zu bearbeiten.

 

1.2.1 Analyse einer Datei mit Select-String

Technet:  Select-String

Das cmdlet Select-String ist eine einfache Möglichkeit Dateien oder Strings nach Mustern zu durchsuchen und Treffer anzuzeigen. Mit Select-String können auch reguläre Ausdrücke verwendet.

"Mit dem Cmdlet "Select-String" werden Text und Textmuster in Eingabezeichenfolgen und -dateien gesucht. Sie können es wie Grep in UNIX und Findstr in Windows verwenden.
" (aus der Onlinehilfe)

Beispiel 1: Alle Zeilen mit einem Suchbegriff aus einer Datei filtern

$FilePath="c:\windows\windowsupdate.log"
$OutPath="c:\temp\errors.log"
$Pattern="Error"

$a=Select-String -Path $FilePath -pattern $Pattern | ft linenumber,line -auto
$a
#$a |Out-File -filepath $OutPath -encoding default #Ausgabe in ein File

#mögliche Ausgabe gekürzt

LineNumber Line          
---------- ----  
       617 2011-12-16    19:57:17:165     356    b0c    AU      # ... = No, error = 0x00000000
       955 2011-12-17    12:25:10:947     396    41c    AU    WARNING: ..., error = 0x80070057    
       956 2011-12-17    12:25:10:947     396    41c    AU    WARNING: ..., error = 0x80070057  

Wenn man die Zeilen nicht mehr weiterbearbeiten möchte, ist dies ein einfacher Weg, um Informationen zu finden.

Beispiel 2: Alle Dateien, die einen Suchbegriff enthalten, finden

$FilePath="c:\windows\"
$Pattern="Error"

$a=Get-ChildItem $FilePath *.log | Select-String -Pattern $Pattern -list
$a | Format-Table Filename,Linenumber -Auto

#mögliche Ausgabe

Filename                LineNumber
--------                ----------
DtcInstall.log                   1
TSSysprep.log                    7
WindowsUpdate.log              617

 

1.2.2 Zeilenweise Bearbeiten einer Datei (Streamreader - ReadLine)

Beispiel 1: Zeilenweises Parsen und Analysieren einer Datei

$FilePath="c:\temp\file_200kB.txt"
$LineNumber=0
$SearchString

$StreamReader = new-object System.IO.StreamReader($FilePath)
While ($StreamReader.Endofstream -eq $False){
  $LineNumber+=1
  $Line=$StreamReader.ReadLine()
  if($Line -like "*error*"){
    "LineNumber: {0} -- {1}" -f $LineNumber,$Line
   }
}

#mögliche Ausgabe

LineNumber: 5 -- 25.12.2011 19:04    oRtE 6Xtv Xgd Error: 250 s5sC aC9pvyr  juOV 03 q9t7TNsfJehlJG9op8q
LineNumber: 7 -- 25.12.2011 19:04   6Jr4ueGiHJ Error: 250  RGBvgL Nhvg2cVOIa yylmXIBiwW2ZP hSmNbdXK1

Anmerkung 1:
Das Beispiel parst zeilenweise "$StreamReader.ReadLine()" durch die Datei und gibt die entsprechende Zeile aus, wenn die Bedingung "($Line -like "*error*")" erfüllt ist.

Anmerkung 2:
Der Operator -like nicht mit dem Operator -contains verwechseln. Mit contains prüft man ein Array ab, ob eines der Elemente einen bestimmten Inhalt hat. siehe Technet:  about_comparison_operators

Anmerkung 3:
Der Operator -like unterstützt einige einfache Platzhalter wie "*", "?" oder "[a-d]". Um den vollen Umfang an regulären Ausdrücken nutzen zu können,  muss man den Operator -match verwenden.

 

1.3 gesamten Text auf einmal bearbeiten (StreamReader - ReadAllText)

Beispiel 1: Ersetzen eines Suchbegriffs in der gesamten Datei

$FilePath="c:\temp\file_20kb.txt"
$a = [IO.File]::ReadAllText($FilePath)
$a=$a.replace("Error: 325","Error: 225")
#$a=$a.replace($OriginalString,$ReplaceString)
$a | Out-File -FilePath $Filepath

 

Beispiel 2: Wieoft kommt ein Suchstring in einer Datei vor?

MSDN:  String.IndexOf-Methode (String, Int32)

$FilePath="c:\temp\file_20kb.txt"
$Searchstring="Error"
$ArrPosition=@()
$Position=0

$a = [IO.File]::ReadAllText($FilePath)
while ($position -ne -1){
 $Position=$a.IndexOf($SearchString,$Position+1)
 $Arrposition+=$Position
}

"Anzahl der Treffer: {0}" -f $Arrposition.count
"Position des ersten Treffers: {0}" -f $ArrPosition[0]
"Letztes Arrayelement: {0}" -f $ArrPosition[-1]

#mögliche Ausgabe

Anzahl der Treffer: 70
Position des ersten Treffers: 411
Letztes Arrayelement: -1

- Das erste Element des Arrays "ArrPosition" besitzt den Index 0.

- Das letzte Element des Arrays "ArrPosition", auf das mit $ArrPosition[-1] zugegriffen werden kann, besitzt den Wert "-1". "-1" bedeutet gemäß dem MSDN-Artikel, dass kein weiterer Treffer mehr gefunden wird.

- Die genaue Berechnung der Treffer wäre demnach $ArrPosition.Count + 1 -1.

- Die Methode IndexOf ist casesensitive.

 

1.4 zeilenübergreifende Textanalyse

Die differenziertesten Möglichkeiten eine Datei zu analysieren, bietet Powershell, wenn jede Zeile als Element eines Arrays vorliegt. Hierbei können sogar Zeilen untereinander in Beziehung gesetzt werden.


Beispiel 1: Ausgabe der passenden Zeile, sowie der Vorgänger- und Nachfolgerzeile

$FilePath="c:\temp\File_20kB.txt"
$Pattern="$Pattern="k[a-y]K"
$Lines = get-content -path $filepath -readcount 0

For($i=0; $i -lt $Lines.Count; $i+=1){
   if( $Lines[$i] | Select-String -Pattern $Pattern){
    "Vorgängerzeile: {0}  Text: {1}" -f $($i-1),$Lines[$i-1]
    "Zeile: {0}  Text: {1}" -f $i,$Lines[$i]
    "Nachfolgezeile: {0}  Text: {1}" -f $($i+1),$Lines[$i+1]
    "`n"
      }
}

#mögliche Ausgabe

Vorgängerzeile: 29  Text: 25.12.2011 19:04   S11Bs5K dYsSMQIfPIMbtdYqBUP....p7Z 4 Error: 250 nxLvoQxC9n8X
Zeile: 30  Text: 25.12.2011 19:04   R..68u Wf TQXmjX  J5syQBvyFB19azNBR  JO8o RjopNn 8SKUWC 4VCoZL2
Nachfolgezeile: 31  Text: 25.12.2011 19:04tfuTEsNIfl Vi0CjxIIBwxc25XNyPoH16....vgv80nd3pAH U wo7eTCLh b

 

Beispiel 2a: Suchen nach Textstellen, bei denen ein Suchbegriff in zwei aufeinanderfolgenden Zeilen vorkommt

$Path="C:\temp"                #Die Dateien in diesem Verzeichnis wird analysiert
$FileFilter="File_20KB.txt"   #Hier können auch Platzhalter angegeben werden z.B. "File*.txt"
$SearchString="*Error: 225*"   #Hier können reguläre Ausdrücke verwendet werden z.B. "E[q-s]ror: 225"
$OutPutFile="Result.log"

Remove-Item $Path\$OutPutFile -EA 0 #-Confirm #Löschen eines evtl. vorhandenen Files nach Bestätigung

Function Analyze{
 param($Fullpath,$OutputFile,$SearchString)
 
 $Value="{0} {1}" -f $FullPath,[Environment]::NewLine #Dateiname über die Treffer schreiben
 
 $Treffer=@()                   #Array mit den Zeilennummern, die den Searchtext enthalten
 
 $Lines=@(get-content -path $Fullpath) #Array mit allen Zeilen der Datei
 $Lines[-1]="Dummy"                    #Vorgängerzeile der ersten TextZeile
 
 $Lines | ForEach{                     #Jede Zeile (=jedes Element des Arrays) untersuchen
   $LineNumber +=1                     #Zeilennummer
   if($_ -like $SearchString){         #Prüfen, ob $Suchstring in der Zeile enthalten ist
    $Treffer+=$LineNumber              #Füge die Zeilennummer dem Array Treffer hinzu
   }#if
 }#Lines
 
 for($i=0; $i -le $Treffer.count;$i++){  #Alle Zeilennummern mit Treffern hochzählen
  if($Treffer[$i] -eq $($Treffer[$i-1]+1)) #Wenn zwei Zeilennummern mit Treffern aufeinander folgen
  {
    $Value += "Zeile {0}: {1} {2}" -f $Treffer[$i-1],$Lines[$Treffer[$i]-2],[Environment]::NewLine
    $Value += "Zeile {0}: {1} {2}" -f $Treffer[$i],$Lines[$Treffer[$i]-1],[Environment]::NewLine
  
    $Value        #Eventuell auskommentieren, wenn keine Bildschirmausgabe gewünscht ist
    Add-Content -Path $Path\$OutPutFile -Value $Value    #Eventuell auskommentieren, wenn keine Fileausgabe gewünscht ist                              
    Remove-Variable Value -EA 0 
    }#if
  }#for
 
}#function
 
$LogFiles=get-childitem -path $Path -filter $FileFilter
$LogFiles | ForEach{
  Analyze -FullPath $_.FullName -OutPutFile $OutPutfile -SearchString $SearchString #Funktionsaufruf
  }  

#mögliche Ausgabe

C:\temp\File_20Kb.txt
Zeile 9: 30.12.2011 22:13   aaa q77wwulC8pOT 6k49hZcW dIemH  6AL  YmxZfJbyesUiaB Error: 225 Y5 lDKxg6St1
Zeile 10: 30.12.2011 22:13    f5jy C3rlELSsAc SgK 5Cg8pZRj e9t fHV Error: 250 QsSXmO Error: 225 ytLUJtZ2M

Zeile 28: 30.12.2011 22:13   Shw6IejTuQhWr 43Wo uEw Error: 225  9dGEkJL6wjRHAIeFbmCBv8MmXsno D6 vJCZt
Zeile 29: 30.12.2011 22:13   I7ia pxLjc7 Error: 225 F uZVJhhr t gN6DxzcCNT2T gvw 2vZoQKmi0Y130CZ VQnf


Beispiel 2b: Suchen nach Textstellen, bei denen ein Suchbegriff in drei aufeinanderfolgenden Zeilen vorkommt

Ein Beispiel für eine zeilenübergreifendes Suchkriterium.

$Path="C:\temp"                #Die Dateien in diesem Verzeichnis wird analysiert
$FileFilter="File_200KB.txt"   #Hier können auch Platzhalter angegeben werden z.B. "File*.txt"
$SearchString="*Error: 225*"   #Hier können reguläre Ausdrücke verwendet werden z.B. "E[q-s]ror: 225"
$OutPutFile="Result.log"

Remove-Item $Path\$OutPutFile -EA 0 #-Confirm #Löschen eines evtl. vorhandenen Files nach Bestätigung

Function Analyze{
 param($Fullpath,$OutputFile,$SearchString)
  
 $Value="{0} {1}" -f $FullPath,[Environment]::NewLine #Dateiname über die Treffer schreiben
 
 $Treffer=@()                   #Array mit den Zeilennummern, die den Searchtext enthalten
 
 $Lines=@(get-content -path $Fullpath) #Array mit allen Zeilen der Datei
 $Lines[-1]="Dummy"                    #Vorgängerzeile der ersten TextZeile
 $Lines[-2]="Dummy"                    #VorVorgängerzeile der ersten TextZeile. Damit auch die erste Zeile auf Vorgängertreffer geprüft werden kann.
 
 $Lines | ForEach{                     #Jede Zeile (=jedes Element des Arrays) untersuchen
   $LineNumber +=1                     #Zeilennummer
   if($_ -like $SearchString){         #Prüfen, ob $Suchstring in der Zeile enthalten ist
    $Treffer+=$LineNumber              #Füge die Zeilennummer dem Array Treffer hinzu
   }#if
 }#Lines
  
 for($i=0; $i -le $Treffer.count;$i++){  #Alle Zeilennummern mit Treffern hochzählen
  if($Treffer[$i] -eq $($Treffer[$i-1]+1)) #Wenn zwei Zeilennummern mit Treffern aufeinander folgen
  {
    if($Treffer[$i] -eq $($Treffer[$i-2]+2)) #Wenn drei Zeilennummern mit Treffern aufeinander folgen
    {
   
    $Value += "Zeile {0}: {1} {2}" -f $Treffer[$i-2],$Lines[$Treffer[$i]-3],[Environment]::NewLine
    $Value += "Zeile {0}: {1} {2}" -f $Treffer[$i-1],$Lines[$Treffer[$i]-2],[Environment]::NewLine
    $Value += "Zeile {0}: {1} {2}" -f $Treffer[$i],$Lines[$Treffer[$i]-1],[Environment]::NewLine
   
    $Value        #Eventuell auskommentieren, wenn keine Bildschirmausgabe gewünscht ist
    Add-Content -Path $Path\$OutPutFile -Value $Value    #Eventuell auskommentieren, wenn keine Fileausgabe gewünscht ist                               
    Remove-Variable Value -EA 0  
    }#if
  }#if
 }#for
 
}#function
 
$LogFiles=get-childitem -path $Path -filter $FileFilter
$LogFiles | ForEach{
  Analyze -FullPath $_.FullName -OutPutFile $OutPutfile -SearchString $SearchString  #Funktionsaufruf
  }  

#mögliche Ausgabe

Zeile 755: 30.12.2011 22:13   g1Y 6E9mynheBnN7Die 0DrwS B1if6lz949l4oA WnkTD  3l Error: 225 c jq dH  sLVi
Zeile 756: 30.12.2011 22:13   0XcQtYNkzL6EmWqWQejv4Y Z Error: 225  mv5rUs8g j EBCrn 1izldcr91gv8uE 7kco01
Zeile 757: 30.12.2011 22:13   H6 dCO0Vas2 i rBt g4oNFjPuWv qFOBQe7  Error: 225 rcsj LCw s5yWeQ1mLk51U 43 

Zeile 836: 30.12.2011 22:13    Error: 225 HyO3  sFe4GJCs0IqMjRApdhY sNkJyablQfP7hox0  c0vBrsH KZIiA8g t  
Zeile 837: 30.12.2011 22:13   VH wGzyYtQz dx3XbWBOgIajMT svycq6 Kaw Error: 225 M AeH ACl8fWP9v82S53yfYV
Zeile 838: 30.12.2011 22:13   Icr 4nNhyiokejbsCL6 S W 3ib Error: 225 KPXkjARsMG10kepnwe 9XmMuL  b6GYmywZ

Das Skript liest die Datei zeilenweise in ein Array ein. Wird ein Treffer gefunden, so wird nachgesehen, ob in der Vor- und VorVorgängerzeile ebenfalls schon ein Treffer registriert wurde.

 

2 Vergleichsoperatoren

Technet:  about_Comparison_Operators

Powershell bietet mehrere Vergleichsoperatoren an, die man zum Analysieren von Teststrings (=Skalare) oder Arrays, die aus Textelementen bestehen, verwenden kann.

-Match / -NotMatch
-Like / -NotLike
-Contains / -NotContains
-eq / -ne  (equal / notequal)

Die angebotene Vielfalt ist in diesem Fall meiner Meinung nach eher verwirrend, da diese Operatoren untereinander sehr ähneln, sich an bestimmten Stellen dann aber tückisch im Detail unterscheiden.

 

2.1 Die Operatoren -Match, -NotMatch, -CMatch, -IMatch

Der Match Operator prüft eine Zeichenfolge mit einem Pattern, das einen regulären Ausdruck beinhalten kann, auf Übereinstimmung.

Nebenformen des -Match Operators sind:
-IMatch (unterscheidet keine Groß- und Kleinschreibung - incasesensitiv, identisch zu -Match)
-CMatch (unterscheidet Groß- und Kleinschreibung - casesensitiv)
IMatch und CMatch funktionieren einwandfrei, werden aber nicht in der offiziellen Onlinehilfe  about_Comparison_Operators in der Technet aufgeführt und sind damit auch nicht supportet. Daher sollte man nach meiner Meinung bei längerlebigen Skripten wenn möglich auf deren Einsatz verzichten.

Der -Match Operator kann sowohl auf skalare Strings, wie auch auf Arrays anwendet werden. Die Ergebnisse in beiden Fällen unterscheiden sich jedoch, was eine tückische Fehlerquelle sein kann.


Beispiel 1a: Der Matchoperator auf ein Skalar angewendet

"Karl" -Match "K[a-c]r"

#Ergebnis
True


Beispiel 1b: Der Matchoperator auf ein Array angewendet

"Karl","kcrl","Kfrl" -Match "K[a-c]r"
"`n"
$Matches = "Karl","kcrl","Kfrl" -Match "K[a-c]r"
$Matches.Count

#Ergebnis
Karl
karl

2

Auf ein Array angewandt, liefert -match also die Treffer selbst als Array zurück, bei einem Skalar das boolsche Ergebnis

Möchte man sicher gehen, dass immer der Wert zurückgegeben wird, so sollte man das Array explizit definieren und alle weiteren Elemente hinzufügen.

$SearchArray=@("") #Array definieren
$SearchArray+=("Karl")  #Arrayelement hinzufügen

$Matches = $SearchArray -Match "K[a-c]r"
$Matches
$Matches.count

#Ausgabe
Karl
1

Möchte man dagegen Arrays durchsuchen und nur die boolsche Information bekommen, ob ein Element enthalten ist oder nicht, so ist dafür der -Contains Operator die richtige Wahl. 


Beispiel 2: Auf welchen Positionen im Array liegt ein Treffer

$Strings = "Karl","karl","Fritz","Karl" #Arraydefinition

Remove-Variable Position,TrefferIndexes
$TrefferIndexes=@()
$Position=-1 #Array beginnt normalerweise bei 0 zu Zählen

$Strings | Foreach{
 $Position+=1
 if($_ -match "K[a-c]r"){
  $TrefferIndexes +=$Position
  }
 }
 
 $TrefferIndexes

#Ausgabe

0
1
3

Man erhält also ein Array $TrefferIndexes zurück, das die Indexes enthält, bei denen im Array $Strings Treffer vorkommen.


Beispiel 3: Der -NotMatch Operator 

"Karl" -NotMatch "K[b-d]r"
"`n"
"Karl", "Hugo", "Heinz" -Notmatch "Karl"

#Ergebnis

True

Hugo
Heinz

Der -Notmatch liefert also Treffer, wenn die Bedingung nicht zutrifft und damit die inversen Ergebnisse der Beispiele 1a und 1b.

-NotCMatch oder NotIMatch sind nicht im Sprachumfang enthalten und liefern Fehlermeldungen. Hier scheint die Powershell V2.0 noch nicht ganz konsistent zu sein.


Beispiel 4: Der -NotMatch Operator zum Zahlenvergleich

$val=0
 while($val -notmatch 3)
 {
     $val++
     Write-Host $val
 }

#Ergebnis

1
2
3

Mit -Match und -NotMatch können nicht nur Strings verglichen werden, sondern auch Integerzahlen. Überlicherweise werden hierfür jedoch die Operatoren -eq und -ne eingesetzt.

 

2.2 Die Operatoren -Like und -NotLike

-Like und -NotLike sind -Match und -NotMatch sehr ähnlich. Der Unterschied besteht darin, dass -Like / -NotLike die Wildcardoperator "*" oder "?" benötigen, wenn der Suchstring innerhalb eines anderen Strings enthalten ist.

Beispiel 1: Vergleich zwischen -Like und -Match

"abcKarlabc", "Hugo", "Heinz" -Like "*K[a-c]rl*"
"abcKarlabc", "Hugo", "Heinz" -Like "???K[a-c]rl???"
"abcKarlabc", "Hugo", "Heinz" -Match "K[a-c]rl"

#Ergebnis

abcKarlabc
abcKarlabc
abcKarlabc

Sowohl -Like und -Match können mit regulären Ausdrücken verwendet werden, was gelegentlich auf einigen Webseiten falsch dargestellt wird.
Ebenso verhalten sich beide Operatoren bei Skalaren und Arrays identisch.

 

2.3 Die Operatoren -Contains und -NotContains

-Contains / -NotContains prüfen auf exakte Übereinstimmung, kennen keine Wildcards, keine regulären Ausdrücke und geben immer einen boolschen Wert zurück.


Beispiel 1: Der -Contains Operator

"*Karl*", "Hugo", "Heinz" -Contains "*Karl*"  
"abcKarlabc", "Hugo", "Heinz" -Contains "*Karl*"
"K[a-c]rl" -Contains "K[a-c]rl"

#Ausgabe

True
False
True

Sowohl auf Skalare, wie auch auf Arrays angewendet, liefert -contains einen "True" oder "False" zurück.
Wildcards wie "*" und reguläre Ausdrücke wie "[a-c]" werden als normale Zeichenfolgen angesehen.

 

2.4 Die Operatoren -eq und -ne

Beim Vergleichen von Texten verhalten sich -eq und -ne sehr ähnlich zu den Operatoren -contains und -notcontains. 

Beispiel 1: Vergleich -eq zu -contains

"Karl", "Hugo", "Heinz" -Contains "Karl" 
"abcKarlabc", "Hugo", "Heinz" -Contains "Karl"
"Karl" -Contains "Kalle"
"`n"
"Karl", "Hugo", "Heinz" -eq "Karl" 
"abcKarlabc", "Hugo", "Heinz" -eq "Karl" #keine Rückgabe
"Karl" -eq "Kalle"

#Ausgabe

True
False
False

Karl
False

Beide Operatoren prüfen auf exakte Übereinstimmung. Die Verwendung von Wildcards oder regulären Ausdrücken ist nicht möglich. 
Wie man im Beispiel sieht, unterscheiden sich jedoch die zurückgegebenen Werte in einigen Fällen.

-eq und ne wird üblicherweise beim Vergleichen von Integerwerten eingesetzt.

 

3 Der System.Text.RegularExpressions-Namespace

MSDN:  System.Text.RegularExpressions-Namespace

3.1 Einleitung

Die Klassen dieses Namespaces bieten viele Möglichkeiten skalare Strings zu analysieren. Im Gegensatz zu den Vergleichsoperatoren aus Kapitel 2 können sie aber nicht auf ganze Arrays angewendet werden, auf einzelne Arrayelemente natürlich schon.

Beispiel 1: Durchsuchen eines Strings auf alle Übereinstimmungen (RegexMethoden Match/ Matches)

$String="sz KarlA Kbrl KcrlNapf 3KdrlZz"
$Pattern="K[a-d]rl" #True bei Karl, Kbrl, Kcrl, Kdrl

$Regex = [Regex]$Pattern 
#$Regex = New-Object System.Text.RegularExpressions.Regex $Pattern  #identisch
#$Regex = [System.Text.RegularExpressions.Regex] $Pattern #identisch
#$Regex = New-Object System.Text.RegularExpressions.Regex($Pattern,"IgnoreCase") #Plus RegexOption: Ignorecase

$Match=$Regex.Match($String) #Instanz der Klasse "System.Text.RegularExpressions.Match"
"Index: {0}  Value: {1}" -f $Match.Index,$Match.Value

While($Match.success -match $true){
  $Match=$Match.Nextmatch()
  if($Match.Success -Match $true){ 
   "Index: {0}  Value: {1}" -f $Match.Index,$Match.Value
   } #if
 } #While

#Ausgabe

Index: 3  Value: Karl
Index: 9  Value: Kbrl
Index: 14  Value: Kcrl
Index: 24  Value: Kdrl

In diesem Beispiel wird eine Instanz der Klasse [Regex] erstellt

MSDN:  Match-Methode

MSDN:  NextMatch-Methode

RegexOptions: siehe Kapitel 3.3

 

3.2 Regex-Klasse

MSDN:  Regex-Klasse

Wie man in dem Link sehen kann, besitzt die Regex-Klasse für viele Aufgaben, sowohl statische (zu erkennen am roten "S"), wie nicht statische gleichlautende Methoden. Beispiele sind die Methoden "Match", "Matches" oder "Replace". 

Mit dem cmdlet Get-Member und dem Positionsparameter bekommt man natürlich ebenso die statischen, wie nichtstatischen Methoden:

Beispiel 1a: statische Methoden der Klasse Regex

[Regex] | Get-Member -Static

#Ausgabe gekürzt
  TypeName: System.Text.RegularExpressions.Regex

Name              MemberType Definition    
----              ---------- ----------  

IsMatch           Method     static bool IsMatch(string input, string pattern), static bool IsMatch(string input, string pattern,...
Match             Method     static System.Text.RegularExpressions.Match Match(string input, string pattern), .
Matches           Method     static System.Text.RegularExpressions.MatchCollection Matches(string input, string pattern), ...
ReferenceEquals   Method     static bool ReferenceEquals(System.Object objA, System.Object objB)    
Replace           Method     static string Replace(string input, string pattern, string replacement), ...
Split             Method     static string[] Split(string input, string pattern),    


Beispiel 1b: Instanzbasierte Methoden der Klasse Regex

[Regex] | Get-Member -MemberType Method 

#Ausgabe gekürzt
   TypeName: System.String

Name             MemberType Definition
----             ---------- ----------
Clone            Method     System.Object Clone()
CompareTo        Method     int CompareTo(System.Object value), int CompareTo(string strB)
Contains         Method     bool Contains(string value)
CopyTo           Method     System.Void CopyTo(int sourceIndex, char[] destination, int destinationIndex
EndsWith         Method     bool EndsWith(string value), bool EndsWith(string value, System.StringCompar
IndexOf          Method     int IndexOf(char value), int IndexOf(char value, int startIndex), int IndexO
IndexOfAny       Method     int IndexOfAny(char[] anyOf), int IndexOfAny(char[] anyOf, int startIndex),
Insert           Method     string Insert(int startIndex, string value)
IsNormalized     Method     bool IsNormalized(), bool IsNormalized(System.Text.NormalizationForm normali
LastIndexOf      Method     int LastIndexOf(char value), int LastIndexOf(char value, int startIndex), in
LastIndexOfAny   Method     int LastIndexOfAny(char[] anyOf), int LastIndexOfAny(char[] anyOf, int start
Normalize        Method     string Normalize(), string Normalize(System.Text.NormalizationForm normaliza
PadLeft          Method     string PadLeft(int totalWidth), string PadLeft(int totalWidth, char paddingC
PadRight         Method     string PadRight(int totalWidth), string PadRight(int totalWidth, char paddin
Remove           Method     string Remove(int startIndex, int count), string Remove(int startIndex)
Replace          Method     string Replace(char oldChar, char newChar), string Replace(string oldValue,
Split            Method     string[] Split(Params char[] separator), string[] Split(char[] separator, in
StartsWith       Method     bool StartsWith(string value), bool StartsWith(string value, System.StringCo
Substring        Method     string Substring(int startIndex), string Substring(int startIndex, int lengt
ToCharArray      Method     char[] ToCharArray(), char[] ToCharArray(int startIndex, int length)
ToLower          Method     string ToLower(), string ToLower(System.Globalization.CultureInfo culture)
ToLowerInvariant Method     string ToLowerInvariant()
ToString         Method     string ToString(), string ToString(System.IFormatProvider provider)
ToUpper          Method     string ToUpper(), string ToUpper(System.Globalization.CultureInfo culture)
ToUpperInvariant Method     string ToUpperInvariant()
Trim             Method     string Trim(Params char[] trimChars), string Trim()
TrimEnd          Method     string TrimEnd(Params char[] trimChars)
TrimStart        Method     string TrimStart(Params char[] trimChars)

Man sieht, dass diese Liste der instanzbasierten (=nicht-statischen) Methoden doch länger ist, als die Liste der statischen Methoden. 
Es gibt Methoden, die sowohl in der statischen, wie auch in der nicht statischen Liste vorkommen, etwa Match, Replace, Split

Die unterschiedliche Anwendung statischer und nicht-statischer Methoden zeige ich im folgenden Kapitel in Beispielen.

3.2.1 Mit der Regex-Klasse instanzbasiert und instanzfrei arbeiten

Möchte man die nicht-statischen Methoden nutzen, so braucht man zuerst eine Instanz der Klasse Regex (Beispiele 1 und 2)
In Beispiel 3 vergleiche ich die beiden Split-Methoden (statisch und nichtstatisch) und den Split-Operator. 


Beispiel 1: Erstellen einer Instanz der Regex-Klasse ohne Optionen

$String="sz karlA Kbrl KcrlNapf 3KdrlZz"
$Pattern="K[a-d]rl" #True für Karl, Kbrl, Kcrl, Kdrl

$Regex = [Regex]$Pattern 
#$Regex = New-Object System.Text.RegularExpressions.Regex $Pattern  #identisch
#$Regex = [System.Text.RegularExpressions.Regex] $Pattern #identisch 

$Match=$Regex.Match($String)
"Index: {0}  Value: {1}" -f $Match.Index,$Match.Value 

#Ausgabe

Index: 9  Value: Kbrl

Ohne die Option "IgnoreCase" (siehe nächstes Beispiel) werden nur Treffer zurückgegeben, die casesensitiv mit dem Suchkriterium übereinstimmen.


Beispiel 2: Erstellen einer Instanz der Regex-Klasse mit Optionen

$String="sz karlA Kbrl KcrlNapf 3KdrlZz"
$Pattern="K[a-d]rl" #True bei Karl, Kbrl, Kcrl, Kdrl  (ohne RegExOptions)

$RegOps=[System.Text.RegularExpressions.RegexOptions]("IgnoreCase","Multiline")
$Regex = New-Object System.Text.RegularExpressions.Regex($Pattern,$Regops)
#$Regex = New-Object System.Text.RegularExpressions.Regex($Pattern,"IgnoreCase")

$Match=$Regex.Match($String)
"Index: {0}  Value: {1}" -f $Match.Index,$Match.Value

#Ausgabe

Index: 3  Value: karl

Mit Option "IgnoreCase" werden auch Treffer zurückgegeben, die nicht casesensitiv mit dem Suchkriterium übereinstimmen.

 
Beispiel 3
a: Einen Satz in Wörter splitten mit der statischen Split-Methode

$String='Mein,guter?Freund*Karl Napf'
$Pattern="\W"
[Regex]::Split($String,$Pattern)

#Ausgabe

Mein
guter
Freund
Karl
Napf

"\W" entspricht einem beliebigen "Nicht-Wortzeichen", also jedem Zeichen, das kein Buchstabe und keine Zahl ist.

Hier wird die statische Form der Split-Methode benutzt. Für statische Methoden muss man die Schreibweise "[Klasse]::Methode(Parameter)" anwenden.  

Technet:  about_operators -> ::-Operator für statische Elemente 

 
Beispiel 3b: Einen Satz in Wörter splitten mit der Split-Methode (Instanz)

$String='Mein,guter?Freund*Karl Napf'
$Pattern="\W"

$Regex = [Regex]$Pattern
#gleichbedeutend mit
#$Regex = [System.Text.RegularExpressions.Regex] $Pattern
#$Regex = new-object System.Text.RegularExpressions.Regex $Pattern

# Anwenden der erzeugten Instanz $rx bzw. der RegularExpression auf einen String 
$Regex.Split($String)

#Ausgabe wie in Beispiel 3a

Seht euch am besten die Split-Methode unter MSDN:  Regex-Klasse -> Methoden, mit den verschiedenen Möglichkeiten an. 


Beispiel 3c: Einen Satz in Wörter splitten mit dem Split-Operator

$String='Mein,guter?Freund*Karl Napf'
$Pattern="\W"

$String -split $Pattern

#Ausgabe wie in Beispiel 3a

Beim Splitten eines Strings ist es Geschmackssache, welche Form man für die Analyse eines Strings verwendet. 

Generell bietet aber die Regex-Klasse mit ihren statischen und nicht statischen Methoden weitergehende Möglichkeiten (Optionen) an, als die Operatoren. 
Man sollte nur den Unterschied zwischen statischen und nicht statischen Methoden einer Klasse verstanden haben.

 

3.2.2 Regex-Methoden

In diesem Kapitel geht es mir um die Einsatzmöglichkeiten der vielen Methoden der Regex-Klasse. Auf die verwendeten Regularexpressions gehe in einem extra Kapitel ein.

Einige Methoden der Regex-Klasse 
MSDN: Regex-Methoden
 

Name

Beschreibung

IsMatch

Überladen. Gibt an, ob der reguläre Ausdruck eine Übereinstimmung in der Eingabezeichenfolge findet. 

Match

Überladen. Durchsucht eine Eingabezeichenfolge nach einem Vorkommen eines regulären Ausdrucks und gibt das exakte Ergebnis als einzelnes Match-Objekt zurück. 

Matches

Überladen. Durchsucht eine Eingabezeichenfolge nach allen Vorkommen eines regulären Ausdrucks und gibt alle erfolgreichen Übereinstimmungen wie bei einem mehrfachen Aufruf von Match zurück. 

Replace

Überladen. Ersetzt in einer angegebenen Eingabezeichenfolge die mit dem Muster für den regulären Ausdruck übereinstimmenden Zeichenfolgen durch eine angegebene Ersetzungszeichenfolge. 

Split

Überladen. Teilt eine Eingabezeichenfolge an den Positionen in ein Array von Teilzeichenfolgen auf, die durch eine Übereinstimmung mit einem regulären Ausdruck definiert werden. 

ToString

Gibt das Muster eines regulären Ausdrucks zurück, das an den Regex-Konstruktor übergeben wurde. (Überschreibt Object..::.ToString()()().)

Unescape

Entfernt alle Escapezeichen aus der Eingabezeichenfolge. 

 

Beispiel 1: Überblick

Dieses Beispiel soll als einfacher Überblick über die Anwendung der Methoden dienen. Die praxisnäheren Beispiele folgen in den nächsten Beispielen

$String="sz karlA Kbrl KcrlNapf 3KdrlZz"
$ReplaceString="Gustav"
$Pattern="K[a-d]rl" #True bei Karl, Kbrl, Kcrl, Kdrl  (ohne RegExOptions)
$Regex = New-Object System.Text.RegularExpressions.Regex($Pattern,"IgnoreCase")

"`nMethode: Ismatch"
$IsMatch=$Regex.IsMatch($String)
"IsMatch={0}" -f $IsMatch

"`nMethode: Match"
$Match=$Regex.Match($String)
"Index: {0}  Value: {1}" -f $Match.Index,$Match.Value

"`nMethode: Matches"
$Matches=$Regex.Matches($String) #Instanz der Klasse "System.Text.RegularExpressions.Match" 
$Matches | foreach{
 "Index: {0}  Value: {1}" -f $_.Index,$_.Value
 }

"`nMethode: Replace(String,String)"
$Replace=$Regex.Replace($String, $ReplaceString)
$Replace

"`nMethode: Replace(String,String,int32)"
$Replace=$Regex.Replace($String, $ReplaceString,2)
$Replace

"`nMethode: Split(String)"
$Pattern=" "
$Regex=[Regex]$Pattern

$Split=$Regex.Split($String)
$Split.Count
$Split[2]

"`nMethode: Split(String,int32)"
$Pattern=" "
$Regex=[Regex]$Pattern

$Split=$Regex.Split($String,3)
$Split

"`nMethode: ToString()"
$Pattern="K[a-d]rl"
$Regex=[Regex]$Pattern
$Regex.ToString()

#Ausgabe

Methode: Ismatch
IsMatch=True

Methode: Match
Index: 3  Value: karl

Methode: Matches
Index: 3  Value: karl
Index: 9  Value: Kbrl
Index: 14  Value: Kcrl
Index: 24  Value: Kdrl

Methode: Replace(String,String)
sz GustavA Gustav GustavNapf 3GustavZz

Methode: Replace(String,String,int32)
sz GustavA Gustav KcrlNapf 3KdrlZz

Methode: Split(String)
5
Kbrl

Methode: Split(String,int32)
sz
karlA
Kbrl KcrlNapf 3KdrlZz

Methode: ToString()
K[a-d]rl

 

Beispiel 2a: Prüfen, ob ein String eine formal gültige IP-Adresse darstellt

In dieser Forums-Diskussion gibt es verschiedene Lösungsansätze mit Regular Expressions. 

 Powershellcommunity.org

Sehr elegant ist hieraus die Lösung von Shay Levy, diese Arbeit der .NetKlasse System.Net.IPAddress zu überlassen und nicht in ellenlangen regulären Ausdrücken unterzugehen.

$String="192.168.33.132"
#$String="2001:0db8:0000:0883:0000:8a2e:0070:7344" #funktioniert mit IP6 genauso)
[Bool]($String -as [IPAddress]

#Ausgabe 
True

Mit dem TypeOperator -as wird versucht, den String in eine IP-Adresse umzuwandeln. Durch das Casten des Ergebnisses in eine boolsche Variable erhält man True oder False als Ergebnis

Technet:  about_Type Operators
MSDN:  IPAddress - Klasse 


Beispiel 2b: Prüfen, ob ein String eine formal gültige email-Adresse darstellt

#[void][reflection.assembly]::LoadWithPartialName("System.Net.Mail") #evtl. erst die Assembly nachladen

$String ="Karl.Napf@Powershellpraxis.de"
[bool]($String -as [System.Net.Mail.MailAddress]) 

#Ausgabe 
True

Hier gilt dasselbe, wie für Beispiel 1a.

MSDN:  MailAddress - Klasse 

Wenn ihr mehr Infos über gültige Mailadressen benötigt, googlet nach den Begriffen "rfc 822 email"

 RFC822 email address validator

 Mail::RFC822::Address: regexp-based address validation (Ansehen!)
 

Beispiel 3: Durchsuchen eines Strings auf alle Übereinstimmungen (Matches-Methode)

$String="sz KarlA Kbrl KcrlNapf 3KdrlZz"
$Pattern="K[a-d]rl" #True bei Karl, Kbrl, Kcrl, Kdrl
$Regex = New-Object System.Text.RegularExpressions.Regex($Pattern,"IgnoreCase") #Plus RegexOption: Ignorecase
$Matches=$Regex.Matches($String) #Instanz der Klasse "System.Text.RegularExpressions.Match" 
$Matches | foreach{
 "Index: {0}  Value: {1}" -f $_.Index,$_.Value
 }

#Ausgabe

Index: 3  Value: Karl
Index: 9  Value: Kbrl
Index: 14  Value: Kcrl
Index: 24  Value: Kdrl


Beispiel 4: Mehrfach Leerzeichen in einfache Leerzeichen umwandeln

$String="sz karlA   Kbrl KcrlNapf   3Kdrl      Zz"
$ReplaceString=" "
$Pattern="  " 
$Regex = New-Object System.Text.RegularExpressions.Regex($Pattern,"IgnoreCase")
$Regex.IsMatch($String)
"Ausgangsstring: $String"

while($Regex.IsMatch($String)){
$String=$Regex.Replace($String, $ReplaceString)
}
"Ergebnisstring: $String"

#Ausgabe

Ausgangsstring: sz karlA   Kbrl KcrlNapf   3Kdrl      Zz
Ergebnisstring: sz karlA Kbrl KcrlNapf 3Kdrl Zz

 

 

 

3.3 Regex-Options

 MSDN: RegexOptions-Enumeration

[regex]::matches("Das,ist?nur*ein<Test","NUR","IgnoreCase")| %{$_.value}

 

4 Verschlüsselung von Strings

 MSDN: ConvertTo-SecureString

 MSDN: ConvertFrom-SecureString

 
Beispiel 1: Ver- und Entschlüsseln eines Strings

$text = "Hallo Karl Napf"
$key = (3,4,7,3,77,2,12,1,2,3,22,4,166,1,32,2)

#---- SecureString aus Klartext erzeugen ------------------

#Moeglichkeit 1 einen Securestring zu ertellen
#$SecureString = new-object Security.SecureString
#$text.ToCharArray() | % { $secureString.AppendChar($_) }

#Moeglichkeit 2 einen Securestring zu ertellen
$SecureString = ConvertTo-SecureString $text -AsPlainText -Force

#Moeglichekeit 3 einen Securestring zu ertellen
#$SecureString = Read-Host -prompt "String eingeben:" -AsSecureString


#---- verschlüsselten StandardString aus SecureString erzeugen -------------------
$EncryptedStandardString = ConvertFrom-SecureString -securestring $SecureString -key $key
write-host "Hash des eingegeben Passworts: $EncryptedStandardString"


#-----SecureString aus verschlüsseltem StandardString erzeugen------------------
$SecureString =  convertto-securestring -string $EncryptedStandardString -key $key


#-----Klartext aus SecureString erzeugen------------------
$CleartextString = [Runtime.Interopservices.Marshal]::PtrToStringAuto([Runtime.Interopservices.Marshal]::SecureStringToBStr($SecureString))
write-host "Entschlüsselter Hash: $CleartextString"

 

die cmdlets ConvertTo-SecureString und ConvertFrom-SecureString verwenden drei Sorten (zwei Klassen) von Strings:

- System.Object.String: "Hallo Karl Napf"
- System.Security.SecureString: $SecureString
- System.Object.String: $EncryptedStandardString

der zur Verschlüsselung verwendete Key $key besteht in diesem Skript aus 128 Bits (16 Bytes). Weitere gültige Werte sind 192 Bits (24 Bytes) und 256 Bits (32 Bytes)

Den Hintergrund zur Verwendung des Securestrings erklärt Lee Holmes hier: Lee Holmes:  HowTo: Use ConvertTo-SecureString and ConvertFrom-SecureString
Möchte man einen String beispielsweise für eine Passworteingabe nutzen, so weigern sich aus Securitygründen die Powershell und .Net einen String zu benutzen, der unverschlüsselt im Memory des RAMs liegt. Daher muß der String zuerst in einen SecureString der Klasse System.Security.SecureString kopiert werden, was mit der Powershell einfach über das cmdlet ConvertTo-SecureString geschieht.

oder MSDN:  SecureString-Klasse
"Stellt Text dar, der vertraulich behandelt werden soll. Aus Datenschutzgründen wird der Text bei Verwendung verschlüsselt und, wenn er nicht mehr benötigt wird, aus dem Computerspeicher gelöscht. Diese Klasse kann nicht vererbt werden."