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.
Beispiel 1a: Erzeugung eines Arrays aus alphanumerischen Zufallstrings
Beispiel 1b: Erzeugen eines Testarrays mit vorgegebenen Zufallswerten
Beispiel 2: Erzeugen einiger Beispieldateien
1.
1.1
1.1.1
Beispiel 1: Wie viele Zeilen hat die Testdatei (get-content)
Beispiel 2: Wie viele Zeilen hat die Testdatei ([System.IO.File])
1.1.2
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
1.2.1
Beispiel 1: Alle Zeilen mit einem Suchbegriff aus einer Datei filtern
Beispiel 2: Alle Dateien, die einen Suchbegriff enthalten, finden
1.2.2
Beispiel 1: Zeilenweises Parsen und Analysieren einer Datei
1.3
Beispiel 1: Ersetzen eines Suchbegriffs in der gesamten Datei
Beispiel 2: Wie oft kommt ein Suchstring in einer Datei vor?
1.4
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.
2.1
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
Beispiel 1: Vergleich zwischen -Like und -Match
2.3
Beispiel 1: Der -Contains Operator
2.4
Beispiel 1: Vergleich -eq zu -contains
3.
3.1
Beispiel 1: Durchsuchen eines Strings auf alle Übereinstimmungen
3.2
Beispiel 1a: statische Methoden der Klasse Regex
Beispiel 1b: Instanzbasierte Methoden der Klasse Regex
3.2.1
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
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.
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
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
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:
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
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:
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:
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:
$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:
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
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
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:
MSDN:
RegexOptions: siehe Kapitel 3.3
3.2 Regex-Klasse
MSDN:
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 3a: 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:
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:
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.
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:
MSDN:
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:
Wenn ihr mehr Infos über gültige Mailadressen benötigt, googlet nach den Begriffen "rfc 822 email"
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
[regex]::matches("Das,ist?nur*ein<Test","NUR","IgnoreCase")| %{$_.value}
4 Verschlüsselung von Strings
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:
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:
"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."