Jump to content

Powershell Script - Suche in Textblöcken


Direkt zur Lösung Gelöst von daabm,

Empfohlene Beiträge

Hallo zusammen,

 

ich bin auf der Suche nach einer Möglichkeit innerhalb einer Textdatei nach einem Text zu suchen und den Textblock / Datensatz, welcher diesen enthält auszugeben.

Der Anfang des Bereichs ist immer eine Zeile mit MSH am Anfang, beim nächsten MSH beginnt ein neuer Bereich (Logfiles einer medizinischen HL7-Schnittstelle < Beispiel unten stark gekürzt).

Wenn innerhalb dieses Blocks der String "7909264350" gefunden wird, den entsprechenden gesamten Block ausgeben (im Beispiel fett markiert).

 

MSH|^~\&|MEL
PID|1||7903313686|
PV1|1||^^^^^^^^2825||||||||||||||||7909260637|
ORC|OK|WKC097U|WKC097U-
OBR|1|WKC097U|WKC097U-1
OBX|1|ST|BI-032^Erreg
OBR|2|WKC097U|WKC097U
OBX|1|ST|BI-685^Staph
OBR|3|WKC097U|WKC097U
OBX|1|ST|BI-PROT1^Bef
NTE|1|L|Kulturelles W
MSH|^~\&|ME
PID|1||7902183713|
PV1|1||^^^^^^^^2938||||||||||||||||7909264350|
ORC|OK|WKC058S|WKC
OBR|1|WKC058S|WKC0
OBX|1|ST|BI-032^Er
OBR|2|WKC058S|WKC0
OBX|1|ST|BI-603^Prot
OBR|3|WKC058S|WKC058S
OBX|1|ST|BI-PROT1^Bef
NTE|1|L|Kulturelles Wach
OBR|4|WKC058S|WKC058S-1|
OBX|1|ST|BI-PROT2^Befund
NTE|1|L|Antibiogramm sie
OBR|5|WKC058S|WKC058S-1|
OBX|1|ST|BI-ICD109^ICD10
NTE|1|L|A40-A41 Bakterie
NTE|2|L|T82.7 Sepsis bei
MSH|^~\&|ME
PID|1||7903126877|
PV1|1||^^^^^^^^2825||||||||||||||||7909265076|
ORC|OK|WKC118D|WKC
OBR|1|WKC118D|WKC1
OBX|1|ST|BI-032^Er
OBR|2|WKC118D|WKC1
OBX|1|ST|BI-673^St
OBR|3|WKC118D|WKC1
OBX|1|ST|BI-PROT1^
NTE|1|L|Zusätzlich
OBR|4|WKC118D|WKC1
OBX|1|NM|BI-ATB^An
OBR|5|WKC118D|WKC1
OBX|1|ST|BI-RES^Me
NTE|1|L|Antibiogra
NTE|2|L|Wirkstoff
NTE|3|L|Penicillin
NTE|4|L|Oxacillin
NTE|5|L|Ampicillin

 

Ideen? - Danke fürs Lesen :-)
Grüße Helmut

Link zu diesem Kommentar

Helmut,

 

Willkommen im MCSEboard.

 

vor 48 Minuten schrieb Helmer:

Ideen?

 

Ne Menge. ;-) 

 

Gegenfrage: Wie weit bist Du denn schon mit Deinem Script/Code? Wo genau bleibst Du stecken? Magst Du den Code zeigen?

 

Ganz allgemein: Nur in ganz seltenen Fällen, ist man mit einer gegeben Aufgabe, der erste. Meistens gibt es bereits Code-Beispiele, die man an seine eigenen Anforderungen anpassen kann. Hast Du denn schon mal gesucht? StackOverflow ist das ein guter Startpunkt. Mit einer schnellen Suche, hab ich schon mal das hier gefunden:

 

https://stackoverflow.com/a/75656236

 

Damit könntest Du ja schon mal anfangen.  Und da gibt's bestimmt auch noch mehr.

 

 

Link zu diesem Kommentar

Hi,

 

ein "Anfang" wäre:

$LogFile = "C:\Install\Test\Log.txt"
$LogContent = Get-Content -Path $LogFile `
    -Raw
$Content = $LogContent -split "(?=MSH\|)" |
    Where-Object { -not [string]::IsNullOrEmpty($_) }

 

Was du jetzt mit $Content anstellst, bleibt dir überlassen. :-)

 

Das ist zwar grob auch im Stackoverflow Artikel zu lesen, aber nunja.. ;-)

 

HTH

Jan

 

BTW: Das "Where-Object" lässt sich bestimmt auch noch umschiffen oder man lebt einfach mit einem leeren ersten Objekt, wenn es stört.

bearbeitet von testperson
Link zu diesem Kommentar

Queue fällt mir spontan ein...  https://powershell.works/2021/09/29/lifo-fifo-with-powershell/

Sobald ich den Marker finde, schiebe ich in die Queue. Wenn ich den nächsten finde und in der Zwischenzeit das Tag vorbeikam, hole ich erst alles wieder raus und "mach irgendwas damit". Kam kein Tag vorbei, schmeiße ich die Queue weg. Dann schiebe ich den aktuellen Marker rein und loope mich so durch. Und wenn die Logs groß sind, noch ein StreamReader dazu. Aber mal sehen wie es weitergeht :-)

Link zu diesem Kommentar

Sehr interessante Ideen, danke euch dafür.
Ich hatte bisher die Geschichte so weit getrieben, dass er mir die MSH Abschnitte aus dem Logfile holt und auf dem Schirm ausgibt.
Misslungen ist mir bisher das Testen der einzelnen Blöcke auf das Vorhandensein des Suchstrings und deren Ausgabe (Blöcke mit vorhandenem Suchstring) in eine Datei.

 

Es ist halt auch nicht wirklich einfach, hinter die Regex Syntax zu steigen....

Get-Content .\oru.log | Out-String | % {[Regex]::Matches($_, "(?<=MSH(.*))((.|\n)*?)(?=MSH(.*))")} | % {$_.Value}

Link zu diesem Kommentar

Ja ok, wenn man das in einen Einzeiler quetscht, wird's unübersichtlich :-)  Warum man wohl nach Get-Content noch Out-String braucht?

Struktrurierte Programme/Skrips sind natürlich länger und manchmal laufen sie auch langsamer. Aber sie sind besser zu warten. Mit Lazy/Greedy Regex machst Dir da keine Freunde, wenn das länger im Einsatz sein soll.

Link zu diesem Kommentar
Am 10.9.2024 um 10:53 schrieb testperson:
$LogFile = "C:\Install\Test\Log.txt"
$LogContent = Get-Content -Path $LogFile `
    -Raw
$Content = $LogContent -split "(?=MSH\|)" |
    Where-Object { -not [string]::IsNullOrEmpty($_) }

Die Ausgabe erzeugt eine schön in "Blöcke" formatierte Datei....
Lässt sich eine Überprüfung der einzelnen Blöcke einbauen, welche nur diese ausgibt, welche auch den Suchstring enthalten?

 

Zitat

Wenn innerhalb dieses Blocks der String "7909264350" gefunden wird, den entsprechenden gesamten Block ausgeben (im Beispiel fett markiert).

 

 

Am 10.9.2024 um 18:41 schrieb daabm:

Queue fällt mir spontan ein...  https://powershell.works/2021/09/29/lifo-fifo-with-powershell/

Sobald ich den Marker finde, schiebe ich in die Queue. Wenn ich den nächsten finde und in der Zwischenzeit das Tag vorbeikam, hole ich erst alles wieder raus und "mach irgendwas damit". Kam kein Tag vorbei, schmeiße ich die Queue weg. Dann schiebe ich den aktuellen Marker rein und loope mich so durch. Und wenn die Logs groß sind, noch ein StreamReader dazu. Aber mal sehen wie es weitergeht :-)

... ich gebe zu, dass ich sehr grün hinter den Ohren bin, was PS-Scripts angeht. Hier sehe ich nicht wirklich eine Umsetzungsmöglichkeit von deiner durchaus Sinnigen Beschreibung in Bezug auf das, was ich über lifo/fifo gefunden habe. Sorry da klickt es nicht bei mir....

Link zu diesem Kommentar
vor 11 Minuten schrieb Helmer:

Lässt sich eine Überprüfung der einzelnen Blöcke einbauen, welche nur diese ausgibt, welche auch den Suchstring enthalten?

Ja, lässt sich einbauen.

 

Da du das

vor 12 Minuten schrieb Helmer:

... ich gebe zu, dass ich sehr grün hinter den Ohren bin, was PS-Scripts angeht.

ja ändern möchtest, was hast du denn bereits unternommen, um die passenden Arrays auszugeben?

Link zu diesem Kommentar
vor 10 Stunden schrieb Helmer:

... ich gebe zu, dass ich sehr grün hinter den Ohren bin, was PS-Scripts angeht.

Das hat noch gar nicht mit Powershell zu tun, sondern mit PAP - Programm Ablauf Plan :-) Bevor ich anfange, etwas umzusetzen, muss ich mir ja die Logik dahinter überlegen, die am besten zum Problem - nein, zur Herausforderung 😂 - passt. Und das führt ganz oft zu völlig unterschiedlichen Ergebnissen.

 

So als Pseudo-Code:
 

InterestingBlocks = [Array]
Foreach Line
  Add Line to Queue
  If Line Contains InterestingString
    InterestingStringFound = true
  End If
  If Line Contains Marker
    # jetzt sind wir beim nächsten Marker "MSH" - haben wir den gesuchten String im letzten Block gefunden?
    If InterestingStringFound
      Add Queue to InterestingBlocks
    End If
    # Danach Queue wegwerfen und von neuem füllen bis zum nächsten "MSH"
    Empty Queue
  End If
Next

 

Statt der Queue kann man natürlich auch ein Hilfsarray verwenden, das alle in der Zwischenzeit gelesenen Zeilen aufnimmt. Queue ist halt deutlich schneller.

bearbeitet von daabm
Link zu diesem Kommentar

Hallo in die Runde,

 

ich habe mich noch ein paar Stunden mit dem „Problem“ beschäftigt und wollte es „einfach“ machen.

Die Beispieldatei von oben wird eingelesen und Zeilenweise in eine Queue geschrieben.

Diese wird als erstes auf das Blockende hin untersucht, wenn dies gefunden, in einer zweiten if Schleife hin auf den Inhalt des Suchbegriffs.

Wenn dieser gefunden, wird der Inhalt der Queue auf den Schirm ausgegeben.

Da die Zeile nach dem Blockende immer der Start eines neuen Blocks ist, sollte auch immer nach dem „Blockende gefunden > Queue gelöscht“ ein neuer Block in der Queue angelegt werden.

 

Ich mache es kurz, es geht nicht (siehe Ausgabe am Ende). Die Queue wird nicht nach den beiden Suchbegriffen durchsucht. Zudem wird die Queue nicht Zeilenweise um Einträge ergänzt. Es wird in der Queue dem ersten Eintrag direkt die nächste Zeile aus der Quelldatei angehängt und keine neue „Zeilen / Einträge“ in der Queue erzeugt.

Beim zweiten "write-host $Queue;" findet keine Ausgabe eines kompletten Blocks statt.

 

$Queue.Contains($SuchString) sucht nicht nach dem $SuchString irgendwo innerhalb der Queue?

 

Die beiden Suchstrings „ORC“ und „PID“ sind nur beispielhaft ausgewählt < machen später keinen Sinn.

 

$Queue = New-Object System.Collections.Queue
$LogFile = "d:\powershell\hl7.log"
$BlockEnde = "ORC"
$SuchString = "PID"

 

foreach ($i in Get-Content -Path $LogFile){
        $Queue.Enqueue($i)
            write-host $Queue  #Nur Kontrolle ob Queue sich füllt - Ausgabe siehe unten
        if ($Queue.Contains($BlockEnde)) {
            if ($Queue.Contains($SuchString)) {
        write-host $Queue #leider keine Ausgabe
        $Queue.Clear()}
        }
        pause  #wieder eine Kontrolle wird Zeilenweise ausgeführt

 

 

kommt zu der Ausgabe:

PS

PS D:\Powershell> .\sort6.ps1
MSH|^~\&|MEL
Drücken Sie die Eingabetaste, um den Vorgang fortzusetzen...: 
MSH|^~\&|MEL PID|1||7903313686|
Drücken Sie die Eingabetaste, um den Vorgang fortzusetzen...: 
MSH|^~\&|MEL PID|1||7903313686| PV1|1||^^^^^^^^2825||||||||||||||||7909260637|
Drücken Sie die Eingabetaste, um den Vorgang fortzusetzen...: 
MSH|^~\&|MEL PID|1||7903313686| PV1|1||^^^^^^^^2825||||||||||||||||7909260637| ORC|OK|WKC097U|WKC097U-
Drücken Sie die Eingabetaste, um den Vorgang fortzusetzen...: 
MSH|^~\&|MEL PID|1||7903313686| PV1|1||^^^^^^^^2825||||||||||||||||7909260637| ORC|OK|WKC097U|WKC097U- OBR|1|WKC097U|WKC097U-1
Drücken Sie die Eingabetaste, um den Vorgang fortzusetzen...: 
MSH|^~\&|MEL PID|1||7903313686| PV1|1||^^^^^^^^2825||||||||||||||||7909260637| ORC|OK|WKC097U|WKC097U- OBR|1|WKC097U|WKC097U-1 OBX|1|S
T|BI-032^Erreg
Drücken Sie die Eingabetaste, um den Vorgang fortzusetzen...: 
MSH|^~\&|MEL PID|1||7903313686| PV1|1||^^^^^^^^2825||||||||||||||||7909260637| ORC|OK|WKC097U|WKC097U- OBR|1|WKC097U|WKC097U-1 OBX|1|S
T|BI-032^Erreg OBR|2|WKC097U|WKC097U
Drücken Sie die Eingabetaste, um den Vorgang fortzusetzen...: 
MSH|^~\&|MEL PID|1||7903313686| PV1|1||^^^^^^^^2825||||||||||||||||7909260637| ORC|OK|WKC097U|WKC097U- OBR|1|WKC097U|WKC097U-1 OBX|1|S
T|BI-032^Erreg OBR|2|WKC097U|WKC097U OBX|1|ST|BI-685^Staph
Drücken Sie die Eingabetaste, um den Vorgang fortzusetzen...: 
MSH|^~\&|MEL PID|1||7903313686| PV1|1||^^^^^^^^2825||||||||||||||||7909260637| ORC|OK|WKC097U|WKC097U- OBR|1|WKC097U|WKC097U-1 OBX|1|S
T|BI-032^Erreg OBR|2|WKC097U|WKC097U OBX|1|ST|BI-685^Staph OBR|3|WKC097U|WKC097U
Drücken Sie die Eingabetaste, um den Vorgang fortzusetzen...: 
MSH|^~\&|MEL PID|1||7903313686| PV1|1||^^^^^^^^2825||||||||||||||||7909260637| ORC|OK|WKC097U|WKC097U- OBR|1|WKC097U|WKC097U-1 OBX|1|S
T|BI-032^Erreg OBR|2|WKC097U|WKC097U OBX|1|ST|BI-685^Staph OBR|3|WKC097U|WKC097U OBX|1|ST|BI-PROT1^Bef
Drücken Sie die Eingabetaste, um den Vorgang fortzusetzen...: 
MSH|^~\&|MEL PID|1||7903313686| PV1|1||^^^^^^^^2825||||||||||||||||7909260637| ORC|OK|WKC097U|WKC097U- OBR|1|WKC097U|WKC097U-1 OBX|1|S
T|BI-032^Erreg OBR|2|WKC097U|WKC097U OBX|1|ST|BI-685^Staph OBR|3|WKC097U|WKC097U OBX|1|ST|BI-PROT1^Bef NTE|1|L|Kulturelles W
Drücken Sie die Eingabetaste, um den Vorgang fortzusetzen...: 
MSH|^~\&|MEL PID|1||7903313686| PV1|1||^^^^^^^^2825||||||||||||||||7909260637| ORC|OK|WKC097U|WKC097U- OBR|1|WKC097U|WKC097U-1 OBX|1|S
T|BI-032^Erreg OBR|2|WKC097U|WKC097U OBX|1|ST|BI-685^Staph OBR|3|WKC097U|WKC097U OBX|1|ST|BI-PROT1^Bef NTE|1|L|Kulturelles W MSH|^~\&|
ME

Link zu diesem Kommentar
Am 12.9.2024 um 08:22 schrieb Helmer:
Am 10.9.2024 um 10:53 schrieb testperson:
$LogFile = "C:\Install\Test\Log.txt"
$LogContent = Get-Content -Path $LogFile `
    -Raw
$Content = $LogContent -split "(?=MSH\|)" |
    Where-Object { -not [string]::IsNullOrEmpty($_) }

Die Ausgabe erzeugt eine schön in "Blöcke" formatierte Datei....
Lässt sich eine Überprüfung der einzelnen Blöcke einbauen, welche nur diese ausgibt, welche auch den Suchstring enthalten?

 

Ich ergänze mal um ein

$Content -match "7909264350"

 

Sollte der Suchstring noch an anderen Stellen vorkommen können, muss halt noch ein bisschen RegEx drumrum.

Link zu diesem Kommentar
vor 9 Stunden schrieb Dukel:

Wenn du es einfach haben willst, lass die Queue weg.
 

$LogFile = "d:\powershell\hl7.log"
$BlockEnde = "ORC"
$SuchString = "PID"

$found = $false

foreach ($i in Get-Content -Path $LogFile){

   if($i -contains $SuchString){
      $found = $true
   }

   if($i -contains $BlockEnde){
      $found = $false
   }

   if($found){
      $i
   }
} 

 

Moin,

 

-contains funktioniert nicht bei Strings, aber von der Logik her kann das so gehen, mit -like, -match oder .StartsWith(). Wenn man die letzte Zeile (mit "ORC") mit ausgegeben haben möchte, müssen die zweite und die dritte Bedingung vertauscht werden.

Link zu diesem Kommentar

Schreibe einen Kommentar

Du kannst jetzt antworten und Dich später registrieren. Falls Du bereits ein Mitglied bist, logge Dich jetzt ein.

Gast
Auf dieses Thema antworten...

×   Du hast formatierten Text eingefügt.   Formatierung jetzt entfernen

  Only 75 emoji are allowed.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Editor-Fenster leeren

×   Du kannst Bilder nicht direkt einfügen. Lade Bilder hoch oder lade sie von einer URL.

×
×
  • Neu erstellen...