Ich habe es schon mit der Bash, der Python und per Hand gemacht…. Richtig es ist die Rede von dem sortieren von validieren von Listen, hier im speziellen eine eMail Liste. Nun wird es mal Zeit das ich mir auch mal RegEx unter der Powershell anschauen. Ja ich weiß, das es dank dem .net Framework viel einfacher geht[1][2], aber da lernt man ja nix
Der erste Schritt war es also in google “eMail Liste” einzugeben und bisschen Material zu besorgen. Zwei Klicks und schon hatte ich was ich wollte. Der zweite Schritt – RegEx erstellen. HAAALT STOP! Da gibt es doch sogar was ganz offizielles. Das RFC 2822 beschreibt ein RegEx welches auf ALLE eMail Adressen passt deren Syntax richtig ist. Also auch auf sowas [email protected], mitarbeiter@contoso, user@[128.128.128.128]. Im Prinzip unnütze, wenn man es aber etwas ergänzt ganz brauchbar.
1
|
$Pattern = "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+(?:[A-Z]{2}|xxx|cat|cc|ss|rh|de|com|org|net|edu|gov|mil|to|tv|fm|biz|info|mobi|name|aero|asia|jobs|museum)\b" |
Es sind zwar noch lange nicht alle TLDs aufgenommen, aber das Prinzip sollte klar sein.
Der nächste nahliegende Schritt auf dem Weg zu einer validen Liste ist das sortieren und das entfernen von Duplikaten. Da macht es PS mir echt einfach.
1
|
$Sorting = Get-Content $Quelldatei | Sort-Object -unique |
Diese Zeil ließt mit Get-Content aus der Quelldatei alles in die Variable $Sorting. Das pipe ich dann weiter zu dem cmdlet Sort-Objekt. Sort-Objekt sortiert das ganzen dann fein nach Alphabet. Der Parameter -unique sorgt dafür das doppelte Einträge nicht beachtet werden. Jetzt komme ich wieder zu meiner geliebten foreach Schleife
1
2
3
4
5
6
7
8
9
10
11
12
13
|
$Pattern = "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+(?:[A-Z]{2}|xxx|cat|cc|ss|rh|de|com|org|net|edu|gov|mil|to|tv|fm|biz|info|mobi|name|aero|asia|jobs|museum)\b" $Sorting = Get-Content $Quelldatei | Sort-Object -unique foreach ( $email in $Sorting ) { if ( $email -match $Pattern ) { $email | add-content $Zieldatei } } Write-Host "Done..." |
Die Foreach Schleife läuft solange bis alle eMail-Adressen(bzw. alle Zeilen in $Sorting) abgearbeitet sind. Unterdessen prüft die IF-Schleife ob der String $email mit den Regex $Pattern matcht. Wenn er das tut, wird der String der nun als korrekte eMail validiert wurde, mit dem cmdlet add-content in die $Zieldatei geschrieben.
Vielleicht ist es ja jemand aufgefallen, aber ich habe die Quell- zu Zieldatei nicht in das Skript geschrieben. Dafür gibt es auch einen guten Grund – Parameter. In diesem Skript muss der Benutzter die Pfade zu den Dateien als Parameter anhängen. Parameter können direkt in den Funktionsnamen geschrieben werden, das sähe so aus:
1
2
3
4
|
function Funktion1 ([string] $Parameter1 ,[int] $Parameter2 ) { # Funktion } |
Aber in meinem Fall will ich die Parameter keiner Funktion im herkömmlichen Sinne übergeben sondern sie Quasi in das Skript pumpen. Das ganze setzt sich dann etwa so zusammen.
1
2
3
4
5
6
7
8
|
param([string] $Quelldatei = $null ,[string] $Zieldatei = $null ) Write-Host "" write-host "" write-host "" Write-Host "" Write-Host "" Write-Host "" Write-Host "" |
Wenn man das Script jetzt startet hagelt es Fehler und der Benutzer weiß erstmal nicht warum. Um das zu vermeiden greifen ich hier auf die String Klasse bzw. auf die String.IsNullOrEmpty Methode des .net Frameworkes zurück.
1
2
3
4
5
6
7
8
9
10
11
12
|
if( [string]::IsNullOrEmpty( $Quelldatei ) -or [string]::IsNullOrEmpty( $Zieldatei ) ) { Write-Host "Bedingung:" Write-Host " powershell.exe .\Sort.ps1 [-Quelldatei] '' [-Zieldatei] ''" Write-Host "" Write-Host "" Write-Host "Beispiel:" Write-Host " powershell.exe .\Sort.ps1 -Quelldatei 'C:\Unsortiert.txt' -Zieldatei 'C:\Sortiert.txt'" Write-Host "" Write-Host " .\Sort.ps1 -Quelldatei 'C:\Unsortiert.txt' -Zieldatei 'C:\Sortiert.txt'" Write-Host "" exit -1 } |
Ich denke die Schleife erklärt sich von selbst -> wenn die $Quelldatei xor $Zieldatei $null oder leer ist, wird die Bedienungsanleitung angezeigt und das Script beendet.
Wenn man es ganz genau nimmt muss man noch paar Exceptions abfangen falls der User mal eine falsche Eingabe macht – find ich übertrieben, sonst hätte ich ja auch gleich irgendwas in C# machen können.
Von der Performance her kann man es nicht mit Standalone Anwendungen vergleichen aber für ein Script rennt es nach einer halben Minute Aufwärmzeit recht gut.
Hier nochmal der komplette Code ohne jegliches Bugtracing etc. xD
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
param([string] $Quelldatei = $null ,[string] $Zieldatei = $null ) Write-Host "" write-host "" write-host "" Write-Host "" Write-Host "" Write-Host "" if( [string]::IsNullOrEmpty( $Quelldatei ) -or [string]::IsNullOrEmpty( $Zieldatei ) ) { Write-Host "Bedingung:" Write-Host " powershell.exe .\Sort.ps1 [-Quelldatei] '' [-Zieldatei] ''" Write-Host "" Write-Host "" Write-Host "Beispiel:" Write-Host " powershell.exe .\Sort.ps1 -Quelldatei 'C:\Unsortiert.txt' -Zieldatei 'C:\Sortiert.txt'" Write-Host "" Write-Host " .\Sort.ps1 -Quelldatei 'C:\Unsortiert.txt' -Zieldatei 'C:\Sortiert.txt'" Write-Host "" exit -1 } $Pattern = "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+(?:[A-Z]{2}|xxx|cat|cc|ss|rh|de|com|org|net|edu|gov|mil|to|tv|fm|biz|info|mobi|name|aero|asia|jobs|museum)\b" #$Pattern = "\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b" $Sorting = Get-Content $Quelldatei | Sort-Object -unique foreach ( $email in $Sorting ) { if ( $email -match $Pattern ) { Write-Host $email $email | add-content $Zieldatei } } Write-Host "Done..." |