none
Suchbegriffe aus Textdatei auf Textdateien anwenden und Treffer in neue Textdatei schreiben RRS feed

  • Frage

  • Hallo Zusammen,

    ich habe das folgende Problem:

    In einer Textdatei gibt es 4000 Suchebgriffe, diese sollen nun auf etwa 50.000 Textdateien angewandt werden. Sollte ein Suchbegriff in einer Textdatei auftauchen, sollen die Namen der entsprechenden Dokumente zunächst in eine separate Textdatei geschrieben werden, die wiederrum den Suchbegriff als Namen trägt. Ich hoffe dass das ganze mit Powershell einigermaßen performant abläuft. Ein entsprechendes Batch-Skript ist leider zu langsam.

    Bislang bin ich zu dem folgenden Code gekommen:

    foreach($mail in "mails.txt") {
    Get-ChildItem -recurse -include *.eml | Select-String -SimpleMatch $mail > "$mail.txt"
    }
    

    Leider funktioniert das ganze nicht sehr gut. Die zu durchsuchenden Dateien befinden sich in 3 Unterordnern, daher die rekursive Abfrage. Ich brauche nur den Dateinamen des Suchtreffers, da in einem weiteren Schritt diese Dateien als PDF gedruckt werden sollen.

    Das erste problem scheint schon, dass ich mit der ersten Schleife gar nicht alle einzelnen Suchwörter (Zeilen) in die Variable bekomme.

    Wäre sehr nett wenn ihr mich etwas unterstützen könntet!

    danke!

    

    

    Mittwoch, 15. Juli 2015 08:57

Antworten

  • Also es scheint tatsächlich ein Problem mit dem Dateinamen zu sein, da er ein paar Mails ordentlich durchläuft und dann bei folgendem Aufbau Probleme hat:

    

    100205-20141218-Cron _root@a19s09_   [ -x _usr_.eml

    Das problem scheint die eckige Klammer zu sein. Gibt es da eine Lösung, außer die aus allen Dateinamen zu entfernen :-)

    €: Ich habe das Problem jetzt gelöst. Eckige Klammern sind als Platzhalter in Powershell definiert. Wenn man mit variablen Dateinamen arbeitet, sollte man den Paramter -LiteralPath nehmen, dann funktioniert alles.

    Der Code ist jetzt etwas gekürzt und sieht wie folgt aus:

    # Suchbegriffe und einzelne Mails laden
    $begriffe = Get-Content mails.txt 
    $mails = Get-ChildItem '.\' -Include *.eml -Recurse
    
    #äußere Schleife arbeitet alle Suchbegriffe ab
    
    Foreach ($begriff in $begriffe){
    
        #innere Schleife wendet Suchbegriff auf Mail an
        Foreach($mail in $mails){
    
            #abgleich, ob suchwort in datei vorhanden ist
           
            if(Get-Content -LiteralPath $mail | select-string -SimpleMatch $begriff){
                echo $mail.Name >> $begriff.txt
            }       
        }
    }

    Ich könnte das ganze aber eigentlich noch beschleunigen, denn im Prinzip muss man ja nur im Adressbereich des E-Mail-Header nach Mails suchen. D.h., sobald der String "content-type" oder ähnliches kommt, könnte die suche im Text abgebrochen werden, da die Mail dann sicherlich nicht mehr enthalten ist. Kann man das irgendwie sinnvoll implementieren?

    Dienstag, 21. Juli 2015 08:17

Alle Antworten

  • Also ich hab meinen Code jetzt überarbeitet und gehe wie folgt vor:

    Get-Content mails.txt | Foreach-Object ($_){
    New-Item .\$_.txt -type file
    Get-ChildItem -recurse -include *.eml | Select-String -Simplematch $_ >> $_.txt
    Write-Output -------
    }
    Jetzt klappt zumindest, dass jeder Suchbegriff einzeln aus der Textdatei gelesen wird. Es wird ein gleichnamiges File angelegt. Nur funktioniert das Zuweisen zu dieser datei leider überhaupt nicht. Gebe ich statt der Variablen feste Werte ein klappt es wunderbar. Ist die Zuweisung >> §_.txt evtl. nicht korrekt?
    Mittwoch, 15. Juli 2015 10:35
  • Hi,

    ich hoffe, dass ich das richtig verstanden habe - hier mein Ansatz:

    function Get-EmailSuche
    {
        # suchwörter und mails laden
        $AlleSuchwoerter = Get-Content 'C:\temp\technet-mails\mails.txt' 
        $AlleMails = Get-ChildItem 'C:\temp\technet-mails' -Include *.eml -Recurse
    
        # für jede mail in den ordnern
        foreach ($Mail in $AlleMails){
            # mailinhalt in var laden
            $MailInhalt = Get-Content $Mail
            
            # für jedes suchwort in der suchwortliste
            foreach ($Suchwort in $AlleSuchwoerter){
                
                # wenn das suchwort im mailcontent gefunden wird - name ausgeben
                if ($MailInhalt | Select-String -SimpleMatch $Suchwort){
                    
                    # $Mail.Name ist nur der Dateiname
                    # $Mail.FullName ist der komplette Dateipfad mit Dateiname
                    
                    # $Mail.Name
                    $Mail.FullName
                }
            }
        }
    }
    
    $temp = Get-EmailSuche
    $temp | Out-File 'C:\temp\technet-mails\Report.txt'

    Die große Frage ist nun, wie performant das ist. Diese function geht jetzt eine email zur zeit durch, aber man könnte das auch mit verschiedenen mitteln parallelisieren. Schau erstmal wie das hier läuft.

    Gruß,

    Donnerstag, 16. Juli 2015 08:55
  • Hallo Alexander,

    danke für den Code, dass sieht echt gut aus.

    Ich habe das etwas angepasst (erst Suchbegriffe, dann Mails als Schleife, damit man fertige Suchbegriffe schon verwenden kann), allerdings erhalte ich immer den folgenden Fehler

    Get-Content : Die dynamischen Parameter für das Cmdlet können nicht abgerufen werden. Das angegebenePlatzhalterzeichenmuster ist ungültig:

    Dann folgt der Dateiname der E-mail. Und danach:

    + $inhalt = get-Content $mail

    + ~~~~~~~~~~~~~~~~~

    + CategoryInfo : InvalidArgument: (:) [Get-Content], ParameterBindingException

    Ich habe auch schon mit absolutem Pfad bei der Zuweisung ($mails=)  gearbeitet, das brachte jedoch keine Verbesserung.

    # Suchbegriffe und einzelne Mails laden
    $begriffe = Get-Content mails.txt 
    $mails = Get-ChildItem '.\' -Include *.eml -Recurse
    
    #äußere Schleife arbeitet alle Suchbegriffe ab
    
    Foreach ($begriff in $begriffe){
    
        Clear-Variable $treffer
        #innere Schleife wendet Suchbegriff auf Mail an
        Foreach($mail in $mails){
    
            #Dateiinhalt in variable laden
            $inhalt = get-Content $mail
    
            #abgleich, ob suchwort in datei vorhanden ist
            if($inhalt | select-string -SimpleMatch $begriff){
                $treffer = $mail.Name
            }
    
           
        }
         if($treffer) {
         get-content $treffer > $begriff.txt
         }
    
    }
    

    Donnerstag, 16. Juli 2015 12:31
  • Hat jemand eine Idee?
    Montag, 20. Juli 2015 05:12
  • Schon mal durch debuggt (z.B.) mit dem ISE? Ist in $mails zur Fehlerzeit was drin und wenn ja was?

    Mal $mail.FullName ausgeben lassen und prüfen bitte.

    Ich kann den Fehler nicht im Geringsten nachstellen.

    Machst du das auf Dateiebene lokal oder wo?

    LG

    Montag, 20. Juli 2015 20:46
  • Also es scheint tatsächlich ein Problem mit dem Dateinamen zu sein, da er ein paar Mails ordentlich durchläuft und dann bei folgendem Aufbau Probleme hat:

    

    100205-20141218-Cron _root@a19s09_   [ -x _usr_.eml

    Das problem scheint die eckige Klammer zu sein. Gibt es da eine Lösung, außer die aus allen Dateinamen zu entfernen :-)

    €: Ich habe das Problem jetzt gelöst. Eckige Klammern sind als Platzhalter in Powershell definiert. Wenn man mit variablen Dateinamen arbeitet, sollte man den Paramter -LiteralPath nehmen, dann funktioniert alles.

    Der Code ist jetzt etwas gekürzt und sieht wie folgt aus:

    # Suchbegriffe und einzelne Mails laden
    $begriffe = Get-Content mails.txt 
    $mails = Get-ChildItem '.\' -Include *.eml -Recurse
    
    #äußere Schleife arbeitet alle Suchbegriffe ab
    
    Foreach ($begriff in $begriffe){
    
        #innere Schleife wendet Suchbegriff auf Mail an
        Foreach($mail in $mails){
    
            #abgleich, ob suchwort in datei vorhanden ist
           
            if(Get-Content -LiteralPath $mail | select-string -SimpleMatch $begriff){
                echo $mail.Name >> $begriff.txt
            }       
        }
    }

    Ich könnte das ganze aber eigentlich noch beschleunigen, denn im Prinzip muss man ja nur im Adressbereich des E-Mail-Header nach Mails suchen. D.h., sobald der String "content-type" oder ähnliches kommt, könnte die suche im Text abgebrochen werden, da die Mail dann sicherlich nicht mehr enthalten ist. Kann man das irgendwie sinnvoll implementieren?

    Dienstag, 21. Juli 2015 08:17
  • Ich habe das ganze jetzt noch so gestaltet, dass die Mails im Trefferfall verschoben werden. Das führt natürlich dazu, dass beim nächsten Durchlauf mit dem nächsten Begriff die entsprechende Mail nicht mehr da ist und ich eine Fehlermeldung erhalte. Kann man denn eine Variable "$mail" aus "$mails" löschen, sodass diese nicht mehr enthalten ist?

    Ein neuladen von Mails ist nicht sehr performant, da dass ganze auf etwa 80.000 Objekte angewandt wird...

    Mittwoch, 22. Juli 2015 05:22
  • Hey, vielleicht hilft es dir ja noch, auch wenn die letzte Antwort schon etwas her ist.

    Schreib doch einfach den ganzen inneren Block in eine Try-Catch-Umgebung. Also etwa so:

    # Suchbegriffe und einzelne Mails laden
    $begriffe = Get-Content mails.txt 
    $mails = Get-ChildItem '.\' -Include *.eml -Recurse
    
    #äußere Schleife arbeitet alle Suchbegriffe ab
    
    Foreach ($begriff in $begriffe){
    
        #innere Schleife wendet Suchbegriff auf Mail an
        Foreach($mail in $mails){
    
    
            #abgleich, ob suchwort in datei vorhanden ist
            
            try{
              if(Get-Content -LiteralPath $mail |select-string -SimpleMatch $begriff){
              echo $mail.Name >> $begriff.txt}
            }catch{}       
        }
    }

    Falls die E-Mail nicht da ist, gibt es einen internen Fehler, der durch den (leeren) Catch-Block abgefangen wird. Du kannst auch irgendwas in dem Catch-Block machen lassen, wie z.B. eine Meldung ausgeben. Ist aber wahrscheinlich unnötig.

    Viele Grüße

    Christoph

    Montag, 27. Juli 2015 12:59